Exporting arbitrary fields in list of dataclass objects to a dict?

Question:

Consider this example:

from dataclasses import dataclass, asdict, astuple

@dataclass
class Player:
  id: int
  name: str
  color: str

players = [
  Player(123, "Alice", "green"),
  Player(456, "Bob", "red"),
  Player(789, "Gemma", "blue"),
]

print("players:", players)
print("players[0] as dict:", asdict( players[0] ) )

The printout I get is:

players: [Player(id=123, name='Alice', color='green'), Player(id=456, name='Bob', color='red'), Player(id=789, name='Gemma', color='bl
ue')]
players[0] as dict: {'id': 123, 'name': 'Alice', 'color': 'green'}

Given that all of the entries above have unique values, what would be an easy way to convert specific pairs of Player class fields from the players into a dict?

For instance, if I want to specify Player.id as key and Player.name as value, then the corresponding dict based on the players list will be:

{
  123: 'Alice',
  456: 'Bob',
  789: 'Gemma'
}

The inverse, where Player.name is taken as key and Player.id as value, would then be:

{
  'Alice': 123,
  'Bob':   456,
  'Gemma': 789
}

… or if I want Player.name as key, and Player.color as value, from the players list I’d get:

{
  'Alice': 'green',
  'Bob':   'red',
  'Gemma': 'blue'
}

Is there an easier way to do this (other that loop through the list, and then construct the dictionary "manually")?

Also, in case some values in the list are not unique, how could such a technique be persuaded to only "save" the first key/value combo it encounters in the dict, and ignore subsequent duplicates of the key?

Asked By: sdbbs

||

Answers:

You can use a dict comprehension. To ignore all but the first occurrence of the value for a specific key, you can reverse the list first.

k = 'id'
v = 'name'
res = {getattr(p, k): getattr(p, v) for p in reversed(players)}
Answered By: Unmitigated

I don’t really like using getattr and i prefer using the attributes directly. this Makes it easier for the IDE to detect types and do autocompletion.

d = {
    player.id: player.name for player in players
}

or if you want a list

id_list = [player.id for player in players]
Answered By: zaki98