1
votes

python: filtre la liste de dict basée sur une autre liste de dict

J'ai deux listes de dictionnaires. Appelons le premier comme dd:

[{'11': {'xx': '259', 'priority': '1', 'channels': '55'}}, {'11': {'xx': '270', 'priority': '11', 'channels': '35'}}, {'11': {'xx': '260', 'priority': '2', 'channels': '35'}}, {'22': {'xx': '300', 'priority': '1', 'channels': '40'}}, {'33': {'xx': '400', 'priority': '1', 'channels': '40'}}]

Les fonctionnalités clés de l'élément dict sont id c'est-à-dire 11,22, 33 et la priorité ie 1,2,3..

Un autre dict est un filtre dict :

dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '4', 'channels': '35'}},
      {'11': {'xx': '260', 'priority': '9', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '11', 'channels': '35'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]

Ce filtre dict a une valeur-clé clé définit le id dans le premier dict dd et la valeur signifie le nombre d'éléments à sélectionner à partir du premier dict dd c.-à-d. {'11': 2} signifie sélectionner les 2 éléments supérieurs du dd en fonction de la priorité code>. Et sélectionnez un seul élément prioritaire dans jj si filter_dct ne contient pas l ' id correspondant.

Ce que j'ai jusqu'à présent, c'est un moyen d'obtenir seulement 1 élément prioritaire du dd basé sur priority:

res = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
          {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
          {'22': {'xx': '300', 'priority': '1', 'channels': '40'}}, # one elem because no record in the filter_dict
          {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
          {'33': {'xx': '500', 'priority': '2', 'channels': '30'}}]

Résultat souhaité que j'essaie d'obtenir:

tmp = {}
for elem in dd:
    tmp.setdefault([*elem][0], []).append(elem)
out = [subl[0] for subl in tmp.values()]
print(out)

MODIFIER:

Les solutions proposées échouent lorsque les éléments sont plus supérieur à 4.

ie pour l'entrée:

filter_dict = [{'11': 2}, {'33': 2}]

la sortie est:

dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '3', 'channels': '35'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]

Ce qui est incorrect.


4 commentaires

dd est-il trié par id et priorité comme dans l'exemple?


@ywbaek jj est trié par priorité mais le id peut varier, c'est-à-dire qu'un autre identifiant après 33 pourrait être 2 ou 2000 .


J'ai fait ce mauvais one-liner: [x pour x dans dd if int (next (iter (x.values ​​())) ['priority']) <= {k: v pour d dans filter_dict pour k, v dans d.items ()}. get (next (iter (x)), 1)] . Cela fonctionne, mais vous ne devriez pas l’utiliser.


Pouvez-vous utiliser d'autres bibliothèques comme les pandas?


4 Réponses :


1
votes

Pas très élégant, mais cela fait le travail, en utilisant un dictionnaire intermédiaire.

[{'11': {'xx': '259', 'priority': '1', 'channels': '55'}}, 
 {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
 {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
 {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
 {'33': {'xx': '500', 'priority': '2', 'channels': '30'}}]
dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '3', 'channels': '35'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]

filter_dict = [{'11': 2}, {'33': 2}]

tmp = {k: d[k] for d in filter_dict for k in d}  # {'11': 2, '33': 2}
out = []
for d in dd:
    id = [*d][0]
    if id not in tmp:
        out.append(d)
        tmp[id] = 0
    elif tmp[id]:
        out.append(d)
        tmp[id] -= 1
    else:
        del tmp[id]

print(out)


1 commentaires

Nicee, cela fonctionne comme prévu. Merci! Pouvons-nous changer les résultats en une liste d'identifiants similaires? comme: res = [{'11': [{'xx': '259', 'priority': '1', 'channels': '55'}, {'xx': '260', ' priority ':' 2 ',' channels ':' 35 '}]}, {' 22 ': [{' xx ':' 300 ',' priority ':' 1 ',' channels ':' 40 '}] }, {'33': [{'xx': '400', 'priority': '1', 'channels': '40'}, {'xx': '500', 'priority': '2' , 'canaux': '30'}]}]



1
votes

Vous pouvez utiliser defaultdict pour vous aider avec ce

[{'11': {'xx': '259', 'priority': '1', 'channels': '55'}}, 
{'11':{'xx':'260','priority': '2', 'channels': '35'}}, 
{'22': {'xx': '300', 'priority': '1', 'channels':'40'}}, 
{'33': {'xx': '400', 'priority': '1', 'channels': '40'}}, 
{'33': {'xx': '500', 'priority': '2', 'channels': '30'}}]

Output

from collections import defaultdict

dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '3', 'channels': '35'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]

res_each = defaultdict(list)
filter_dict = [{'11': 2}, {'33': 2}]
filter_map = {list(i.keys())[0]: list(i.values())[0] for i in filter_dict}
for i in dd:
    res_each[list(i.keys())[0]].append(i)
res = []
for i in [sorted(v, key=lambda x: int(list(x.values())[0]['priority']))[:filter_map.get(k, 1)] for k, v in res_each.items()]:
    res.extend(i)
print(res)


4 commentaires

Frais. J'aime vraiment ça, pouvons-nous regrouper les éléments d'identification similaires? une sortie comme: result = [{'11': [{'xx': '259', 'priority': '1', 'channels': '55'}, {'xx': '260' , 'priority': '2', 'channels': '35'}]}, {'22': [{'xx': '300', 'priority': '1', 'channels': '40' }]}, {'33': [{'xx': '400', 'priority': '1', 'channels': '40'}, {'xx': '500', 'priority': ' 2 ',' chaînes ':' 30 '}]}]


Vous pouvez par imprimer ([{list (i [0] .keys ()) [0]: sum ([list (y.values ​​()) for y in i], [])} for i in [ sorted (v, key = lambda x: list (x.values ​​()) [0] ['priority']) [: filter_map.get (k, 1)] pour k, v dans res_each.items ()]]) à la fin mais ce n'est pas si joli.


la solution casse s'il y a plus de 4 éléments du même id .


J'ai modifié le code et maintenant la sortie est [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}}, {'11 ': {' xx ':' 260 ',' priority ':' 2 ',' channels ':' 35 '}}, {' 22 ': {' xx ':' 300 ',' priority ':' 1 ' , 'channels': '40'}}, {'33': {'xx': '400', 'priority': '1', 'channels': '40'}}, {'33': {' xx ':' 500 ',' priority ':' 2 ',' channels ':' 30 '}}] basé sur le nouveau cas.



1
votes

Je suppose que dd n'est pas du tout trié. J'espère que cela vous aidera.

dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '270', 'priority': '3', 'channels': '35'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]
# firstly sorted by key, then sorted by priority
dd = sorted(dd, key=lambda e: (list(e.keys())[0], int(list(e.values())[0]['priority'])))

filter_dict = [{'11': 2}, {'33': 2}]
filter_dict = {k:v for elem in filter_dict for k,v in elem.items()}
res = []
for i in dd:
    key = list(i.keys())[0]
    if key in filter_dict:
        if filter_dict[key] > 0:
            res.append(i)
            filter_dict[key] -= 1
    else:
        res.append(i)
        filter_dict[key] = 0

for i in res:
    print(i)


2 commentaires

@Sorn Ninh, il échoue avec si la priorité est 11 il semble qu'il ne se compare pas comme int . J'ai modifié ma question pour montrer le cas d'utilisation.


@LeeChun ouais, convertissez simplement 'priority' en type int. J'ai mis à jour la réponse



2
votes

cela a fonctionné pour moi:

[{'11': [{'xx': '259', 'priority': '1', 'channels': '55'}, {'xx': '260', 'priority': '2', 'channels': '35'}]}, {'22': [{'xx': '300', 'priority': '1', 'channels': '40'}]}, {'33': [{'xx': '400', 'priority': '1', 'channels': '40'}, {'xx': '500', 'priority': '2', 'channels': '30'}]}]

Output

dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '4', 'channels': '35'}},
      {'11': {'xx': '260', 'priority': '9', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '11', 'channels': '35'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]

filter_dict = [{'11': 2}, {'33': 2}]

res = []
middle_dict = {}
position = 0
first = True

def sortSecond(val): 
    return val[0]

for dd_one_dict in dd:
    for dict_id, sub_dict in dd_one_dict.items():
        if middle_dict.get(dict_id,False):
            middle_dict[dict_id].append((int(sub_dict['priority']),position))
        else:
            middle_dict[dict_id] = [(int(sub_dict['priority']),position)]
        position += 1
    middle_dict[dict_id].sort(key = sortSecond)

external_counter = 0 
for one_key in middle_dict.keys():
    internal_counter = 2
    for items in middle_dict[one_key]:
        if first:
            res.append({one_key:[dd[items[1]][one_key]]})
            first = False
        else:
            for filter_one_dic in filter_dict:
                if one_key == list(filter_one_dic.keys())[0]:
                    if internal_counter <= filter_one_dic[list(filter_one_dic.keys())[0]]:
                        res[external_counter][one_key].append(dd[items[1]][one_key])
                        internal_counter += 1
                    else:
                        break
    first = True
    external_counter += 1

print(res) 

EDIT:

Voici une approche plus générale qui modifient également le format

{'11': {'xx': '259', 'priority': '1', 'channels': '55'}}
{'11': {'xx': '260', 'priority': '2', 'channels': '35'}}
{'22': {'xx': '300', 'priority': '1', 'channels': '40'}}
{'33': {'xx': '400', 'priority': '1', 'channels': '40'}}
{'33': {'xx': '500', 'priority': '2', 'channels': '30'}}

Sortie:

dd = [{'11': {'xx': '259', 'priority': '1', 'channels': '55'}},
      {'11': {'xx': '260', 'priority': '2', 'channels': '35'}},
      {'11': {'xx': '270', 'priority': '3', 'channels': '35'}},
      {'22': {'xx': '300', 'priority': '1', 'channels': '40'}},
      {'22': {'xx': '303', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '400', 'priority': '1', 'channels': '40'}},
      {'33': {'xx': '500', 'priority': '2', 'channels': '30'}},
      {'33': {'xx': '606', 'priority': '3', 'channels': '30'}}]

filter_dict = [{'11': 2}, {'33': 2}]

res = []

for dd_one_dic in dd:
    for dir_id, priority in dd_one_dic.items():
        if priority['priority'] == '1':
            res.append(dd_one_dic)
        else:
            for filter_one_dic in filter_dict:
                if dir_id == list(filter_one_dic.keys())[0]: 
                    if int(priority['priority']) <= filter_one_dic[list(filter_one_dic.keys())[0]]:
                        res.append(dd_one_dic)

print(*res, sep = '\n')


2 commentaires

Ansres Lara Viana Pouvez-vous modifier votre réponse pour regrouper les éléments d'identification similaires? une sortie comme: result = [{'11': [{'xx': '259', 'priority': '1', 'channels': '55'}, {'xx': '260' , 'priority': '2', 'channels': '35'}]}, {'22': [{'xx': '300', 'priority': '1', 'channels': '40' }]}, {'33': [{'xx': '400', 'priority': '1', 'channels': '40'}, {'xx': '500', 'priority': ' 2 ',' chaînes ':' 30 '}]}]


@LeeChun J'ai fait un changement de format et ajouté un peu plus de généralisation au code, mais je vous recommande de le tester soigneusement avec différents ensembles de données.