2
votes

Comment fusionner un dictionnaire en fonction d'une condition?

Disons que j'ai la liste de dictionnaires suivante:

x_updated=sorted(x, key=lambda x: x.values()[0])
final_merge=[]
merge=[]
for first, second in zip(x_updated, x_updated[1:]):
    if abs(second.values()[0]['start']-first.values()[0]['end'])<25:
        print "its belong to the same column"
        merge=merge+[first.keys()[0]]
    else:
        merge=merge+[first.keys()[0]]
        final_merge=final_merge+[merge]
        merge=[]
merge=merge+[second.keys()[0]]      
final_merge=final_merge+[merge]

Je veux fusionner certains des dictionnaires, la condition est quand la fin du premier dict et le début du prochain dict ont une différence de moins de 20 que ce dont j'ai besoin pour fusionner tous les dict et concaténer tout le texte.

Le résultat devrait ressembler à ceci:

x_new = [{
  '218,312,350': {
    'text': 'profit for year',
    'start': 0,
    'end': 85
  }
}, {
  '370': {
    'text': 'next column',
    'start': 120,
    'end': 130
  }
}, {
  '385': {
    'text': 'next_column',
    'start': 160,
    'end': 169
  }
}]

Je l'ai déjà résolu avec l'approche de base, mais cela n'a pas l'air bien, y a-t-il une solution utilisant itertools ou quelque chose comme ça?

Ce que j'ai essayé

x = [{
  '218': {
    'text': 'profit',
    'start': 0,
    'end': 21
  }
}, {
  '312': {
    'text': 'for',
    'start': 30,
    'end': 60
  }
}, {
  '350': {
    'text': 'year',
    'start': 70,
    'end': 85
  }
}, {
  '370': {
    'text': 'next column',
    'start': 120,
    'end': 130
  }
}, {
  '385': {
    'text': 'next_column',
    'start': 160,
    'end': 169
  }
}]

Et une fois que j'ai final_merge, qui me dit quelle valeur fusionner, il est facile d'ajouter les valeurs. mais pour le code ci-dessus, y a-t-il un moyen simple.Aussi, à la fin, après la boucle, j'ai ajouté manuellement le dernier dict car dans ma situation, le dernier serait toujours une colonne différente, mais que se passe-t-il s'il appartient à la même chose? p>


0 commentaires

3 Réponses :


0
votes

Essayez ceci:

[
  {
    '218,312,350': {
      'text': 'year',
      'start': 0,
      'end': 85
    }
  },
  {
    '370': {
      'text': 'next column',
      'start': 120,
      'end': 130
    }
  },
  {
    '385': {
      'text': 'next_column',
      'start': 160,
      'end': 169
    }
  }
]

O / P:

x=[{'218':{'text':'profit','start':0,'end':21}},
   {'312':{'text':'for','start':30,'end':60}},
   {'350':{'text':'year','start':70,'end':85}},
   {'370':{'text':'next column','start':120,'end':130}},
   {'385':{'text':'next_column','start':160,'end':169}}]

x_new = []
d_keys = []
first_start_value = 0

def merge_dict(d_keys,x,i,first_start_value,current_index_dict_key):
    # remove duplicate list of string
    d_keys = list(set(d_keys))

    # sort list by number
    d_keys.sort(key=int)
    new_key = ','.join(d_keys)

    # update start value
    x[i][current_index_dict_key]['start'] = first_start_value
    dict1 = {new_key: x[i][current_index_dict_key]}
    return  dict1

for i in range(0,len(x)):
    current_index_dict_key = list(x[i].keys())[0]

    #check next index of list is valid
    if i+1 > len(x)-1:
        if len(d_keys) > 0:
            # merge dictionary
            dict1 = merge_dict(d_keys, x, i, first_start_value, current_index_dict_key)
            x_new.append(dict1)
            break

        dict1 = {current_index_dict_key: x[i][current_index_dict_key]}
        x_new.append(dict1)
        break

    next_index_dict_key = list(x[i+1].keys())[0]
    start = x[i+1][next_index_dict_key]['start']
    end = x[i][current_index_dict_key]['end']
    diff = start - end

    #compare current and next list of dicstionary end and start value
    if diff < 20:
        if len(d_keys) <= 0 and i == 1:
            first_start_value = x[i][current_index_dict_key]['start']

        d_keys.append(current_index_dict_key)
        d_keys.append(next_index_dict_key)
    else:

        if len(d_keys) > 0:
            # merge dictionary
            dict1 = merge_dict(d_keys,x,i,first_start_value,current_index_dict_key)
            d_keys = []
            first_start_value = x[i][current_index_dict_key]['start']
        else:
            dict1 = {current_index_dict_key: x[i][current_index_dict_key]}

        x_new.append(dict1)

print(x_new)

p >


0 commentaires

0
votes

Je créerais une classe pour ces objets que vous utilisez:

x_new = [x[0]]
for obj in x[1:]:
    last = x_new[-1]
    if obj.start - last.end > 20:
        x_new.append(obj)
    else:
        last.merge(obj)

Et puis la boucle de code principale sera:

class my_dict:
    __init__(self, id, text, start, end):
        self.id = id
        self.text = text
        self.start = start
        self.end = end

    merge(self, other):
        self.id = "{},{}".format(self.id, other.id)
        self.text = "{} {}".format(self.text, other.text)
        self.end = other.end


0 commentaires

0
votes

Voici ce que je ferais:

Je créerais d'abord des fonctions d'aide:

d= {'id': '385',
    'text': 'next_column',
    'start': 160,
    'end': 169
  }

La première fonction fusionne deux dictionnaires

Le second retourne True si deux dictionnaires doivent fusionner.

Il ne reste plus que la fonction de fusion réelle:

from itertools import zip_longest
def merged_dicts(x):
    actual_merge = []
    last_merged = False
    for d1, d2 in zip_longest(x, x[1:], fillvalue=None):
        if should_merge(d1, d2) and last_merged:
            actual_merge.append(merge(actual_merge.pop(), d2))
        elif should_merge(d1, d2):
            actual_merge.append(merge(d1, d2))
            last_merged = True
        elif last_merged:
            last_merged = False
        else:
            actual_merge.append(d1)
            last_merged = False
    print(actual_merge)

C'est un peu plus lisible bien que ce ne soit pas le cas. t utiliser des fonctions itertool "fantaisistes".

J'envisagerais également de changer l'id du dict pour qu'il soit à l'intérieur du dict interne:

def merge(d1, d2):
    return {",".join([list(d1)[0], list(d2)[0]]): {'text': " ".join([list(d1.values())[0]['text'], list(d2.values())[0]['text']]), 'start': list(d1.values())[0]['start'], 'end': list(d2.values())[0]['end']}}

def should_merge(d1, d2):
    if (d1 is None) or (d2 is None):
        return False
    return abs(list(d1.values())[0]['end'] - list(d2.values())[0]['start']) < 20

C'est un peu moins compliqué et plus propre.


0 commentaires