Ottenere un elenco di valori da un elenco di dadi


185

Ho un elenco di dicts come questo:

[{'value': 'apple', 'blah': 2}, 
 {'value': 'banana', 'blah': 3} , 
 {'value': 'cars', 'blah': 4}]

Voglio ['apple', 'banana', 'cars']

qual'è il miglior modo per farlo?

Risposte:


325

Supponendo che ogni dict abbia una valuechiave, puoi scrivere (supponendo che il tuo elenco sia chiamato l)

[d['value'] for d in l]

Se valuepotrebbe mancare, è possibile utilizzare

[d['value'] for d in l if 'value' in d]

8
Per trattare il valore mancante per una chiave, si può anche usare d.get ("key_to_lookup", "alternate_value"). Quindi, apparirà come: [d.get ('value', 'alt') per d in l]. Se il valore non è presente come chiave, restituirà semplicemente 'alt'.
Abhinav,

Grazie per la soluzione
Ajay Kumar il

Questa "magia" è conosciuta come comprensione della lista docs.python.org/3/tutorial/…
William Ardila

Salvavita! Sto provando / cercando questo per oggetti sqlalchemy da molto tempo. Ora ha funzionato come un [d.value for d in l]ringraziamento :)
krupesh Anadkat

34

Ecco un altro modo per farlo usando le funzioni map () e lambda:

>>> map(lambda d: d['value'], l)

dove l è l'elenco. Vedo in questo modo "più sexy", ma lo farei usando la comprensione della lista.

Aggiornamento: nel caso in cui il "valore" potrebbe mancare come chiave utilizzare:

>>> map(lambda d: d.get('value', 'default value'), l)

Aggiornamento: anche io non sono un grande fan di lambdas, preferisco dare un nome alle cose ... ecco come lo farei con questo in mente:

>>> import operator
>>> map(operator.itemgetter('value'), l)

Vorrei anche andare oltre e creare un'unica funzione che dice esplicitamente ciò che voglio ottenere:

>>> import operator, functools
>>> get_values = functools.partial(map, operator.itemgetter('value'))
>>> get_values(l)
... [<list of values>]

Con Python 3, poiché maprestituisce un iteratore, utilizzare listper restituire un elenco, ad es list(map(operator.itemgetter('value'), l)).


1
Schifoso! La comprensione dell'elenco ha molto più senso qui.
Mike Graham,

4
Se hai intenzione di usare il primo map, usa operator.itemgetter('value'), non a lambda.
agf,

18
[x['value'] for x in list_of_dicts]

o [d.getkey ('value') per d in dict_list]
rplnt

1
@rpInt Um, non esiste nulla come getkey.. vuoi dire d.get('value')? Sarebbe lo stesso della seconda lista di comprensione di @ isbadawi, non di Michal.
agf,

3
Sì, scusa, intendevo ottenere. E lo intendevo come qualcosa che non genererà eccezioni se manca la chiave. Ma la soluzione di @ isbadawi è migliore in quanto get () fornirà None (o qualunque cosa specifichiamo come predefinita) quando manca la chiave.
rplnt,

5

Per un caso molto semplice come questo, una comprensione, come nella risposta di Ismail Badawi, è sicuramente la strada da percorrere.

Ma quando le cose diventano più complicate e devi iniziare a scrivere comprensioni multi-clausola o nidificate con espressioni complesse in esse, vale la pena cercare altre alternative. Esistono diversi modi (quasi) standard per specificare ricerche in stile XPath su strutture nict dict-and-list, come JSONPath, DPath e KVC. E ci sono belle librerie su PyPI per loro.

Ecco un esempio con la libreria denominata dpath, che mostra come può semplificare qualcosa di un po 'più complicato:

>>> dd = {
...     'fruits': [{'value': 'apple', 'blah': 2}, {'value': 'banana', 'blah': 3}],
...     'vehicles': [{'value': 'cars', 'blah':4}]}

>>> {key: [{'value': d['value']} for d in value] for key, value in dd.items()}
{'fruits': [{'value': 'apple'}, {'value': 'banana'}],
 'vehicles': [{'value': 'cars'}]}

>>> dpath.util.search(dd, '*/*/value')
{'fruits': [{'value': 'apple'}, {'value': 'banana'}],
 'vehicles': [{'value': 'cars'}]}

Oppure, usando jsonpath-ng:

>>> [d['value'] for key, value in dd.items() for d in value]
['apple', 'banana', 'cars']
>>> [m.value for m in jsonpath_ng.parse('*.[*].value').find(dd)]
['apple', 'banana', 'cars']

A prima vista, questo potrebbe non sembrare altrettanto semplice, perché findrestituisce oggetti di corrispondenza, che includono tutti i tipi di cose oltre al solo valore corrispondente, come un percorso direttamente a ciascun elemento. Ma per espressioni più complesse, essere in grado di specificare un percorso come al '*.[*].value'posto di una clausola di comprensione per ciascuno *può fare una grande differenza. Inoltre, JSONPath è una specifica indipendente dalla lingua e ci sono anche tester online che possono essere molto utili per il debug.


1

Penso che semplice come sotto ti darebbe quello che stai cercando.

In[5]: ll = [{'value': 'apple', 'blah': 2}, {'value': 'banana', 'blah': 3} , {'value': 'cars', 'blah':4}]
In[6]: ld = [d.get('value', None) for d in ll]
In[7]: ld
Out[7]: ['apple', 'banana', 'cars']

Puoi farlo con una combinazione di mape lambdapure, ma la comprensione dell'elenco sembra più elegante e pitonica.

Per una lista di input più piccola la comprensione è la strada da percorrere, ma se l'input è davvero grande, immagino che i generatori siano il modo ideale.

In[11]: gd = (d.get('value', None) for d in ll)
In[12]: gd
Out[12]: <generator object <genexpr> at 0x7f5774568b10>
In[13]: '-'.join(gd)
Out[13]: 'apple-banana-cars'

Ecco un confronto di tutte le possibili soluzioni per input più grandi

 In[2]: l = [{'value': 'apple', 'blah': 2}, {'value': 'banana', 'blah': 3} , {'value': 'cars', 'blah':4}] * 9000000
In[3]: def gen_version():
  ...:     for i in l:
  ...:         yield i.get('value', None)
  ...: 
In[4]: def list_comp_verison():
  ...:     return [i.get('value', None) for i in l]
  ...: 
In[5]: def list_verison():
  ...:     ll = []
  ...:     for i in l:
  ...:         ll.append(i.get('value', None))
  ...:     return ll
In[10]: def map_lambda_version():
   ...:      m = map(lambda i:i.get('value', None), l)
   ...:      return m
   ...: 
In[11]: %timeit gen_version()
172 ns ± 0.393 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In[12]: %timeit map_lambda_version()
203 ns ± 2.31 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In[13]: %timeit list_comp_verison()
1.61 s ± 20.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In[14]: %timeit list_verison()
2.29 s ± 4.58 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Come puoi vedere, i generatori sono una soluzione migliore rispetto agli altri, la mappa è anche più lenta rispetto al generatore per la ragione che lascerò all'OP per capire.


1

Segui l'esempio --

songs = [
{"title": "happy birthday", "playcount": 4},
{"title": "AC/DC", "playcount": 2},
{"title": "Billie Jean", "playcount": 6},
{"title": "Human Touch", "playcount": 3}
]

print("===========================")
print(f'Songs --> {songs} \n')
title = list(map(lambda x : x['title'], songs))
print(f'Print Title --> {title}')

playcount = list(map(lambda x : x['playcount'], songs))
print(f'Print Playcount --> {playcount}')
print (f'Print Sorted playcount --> {sorted(playcount)}')

# Aliter -
print(sorted(list(map(lambda x: x['playcount'],songs))))

1

Ottieni valori chiave dall'elenco dei dizionari in Python?

  1. Ottieni valori chiave dall'elenco dei dizionari in Python?

Ex:

data = 
[{'obj1':[{'cpu_percentage':'15%','ram':3,'memory_percentage':'66%'}]},
{'obj2': [{'cpu_percentage':'0','ram':4,'memory_percentage':'35%'}]}]

per d nei dati:

  for key,value in d.items(): 

      z ={key: {'cpu_percentage': d['cpu_percentage'],'memory_percentage': d['memory_percentage']} for d in value} 
      print(z)

Produzione:

{'obj1': {'cpu_percentage': '15%', 'memory_percentage': '66%'}}
{'obj2': {'cpu_percentage': '0', 'memory_percentage': '35%'}}

-2

Un modo molto semplice per farlo è:

list1=['']
j=0
for i in com_list:
    if j==0:
        list1[0]=(i['value'])
    else:
        list1.append(i['value'])
    j+=1

Produzione:

['apple', 'banana', 'cars']

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.