Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing DeepDiff result

I am working with DeepDiff. So I have results like:

local =  [{1: {'age': 50, 'name': 'foo'}}, {2: {'age': 90, 'name': 'bar'}}, {3: {'age': 60, 'name': 'foobar'}}]
online = [{1: {'age': 50, 'name': 'foo'}}, {2: {'age': 40, 'name': 'bar'}}]
ddiff = DeepDiff(local, online)
added, updated = ddiff['iterable_item_added'], ddiff['values_changed']
added = {'root[2]': {3: {'age': 60, 'name': 'foobar'}}}
updated = {"root[1][2]['age']": {'new_value': 90, 'old_value': 40}}

Now, I want to take:

list_indexes_added = foo(added)
list_indexes_updated = foo(updated)

and to obtain:

list_indexes_added = [2]
list_index_updated = [(1,2,'age')]

in this way, I can manipulate the list local and online and in the future update the online table.

I am thinking in regexs, but maybe there is other option.

like image 548
FacundoGFlores Avatar asked Sep 20 '16 13:09

FacundoGFlores


3 Answers

  • One solution can be regex and custom parsing of the matches.

  • Another can be using literal_eval after regex parsing on these strings, if the output format of deepdiff is consistent

    from ast import literal_eval
    import re
    
    
    def str_diff_parse(str_diff):
        return [tuple(literal_eval(y) for y in re.findall(r"\[('?\w+'?)\]", x)) for x in str_diff]
    
    added = {'root[2]': {3: {'age': 60, 'name': 'foobar'}}}
    updated = {"root[1][2]['age']": {'new_value': 90, 'old_value': 40}}
    
    list_indexes_added = str_diff_parse(added)
    list_indexes_updated = str_diff_parse(updated)
    
    print(list_indexes_added)
    print(list_indexes_updated)
    # prints
    #[(2,)]
    #[(1, 2, 'age')]
    

demo : http://ideone.com/3MhTky

  • Would also recommend dictdiffer module, it returns the diff as a consumable python diff object which can be patched to original dictionary to get the updated one or vice versa.
like image 95
DhruvPathak Avatar answered Nov 08 '22 19:11

DhruvPathak


This is what I did:

def getFromSquareBrackets(s):
    return re.findall(r"\['?([A-Za-z0-9_]+)'?\]", s)

def auxparse(e):
    try:
        e = int(e)
    except:
        pass
    return e

def castInts(l):
    return list((map(auxparse, l)))

def parseRoots(dics):
    """
        Returns pos id for list.
        Because we have formmatted [{id:{dic}}, {id:{dic}}]
    """
    values = []
    for d in dics:
        values.append(castInts(getFromSquareBrackets(d)))
    return values

So:

parseRoots({"root[1][2]['age']": {'new_value': 90, 'old_value': 40}})
[[1, 2, 'age']]

Maybe someone can improve it.

like image 3
FacundoGFlores Avatar answered Nov 08 '22 21:11

FacundoGFlores


So, I'd go with something like this:

import re

def foo(diff):
modded = []

for key in diff.keys():
    m = re.search('\[(.+)\]', key)
    modded.append(tuple(m.group(1).split('][')))

return modded

It'll read each key, extract only the indices (whether numeric or string), then slice up the string. Since your desired output indicates a tuple, it'll spit the sequence of indices back as one, then return the whole list of index sets (since diff might have more than one).

This can be golfed down into a one-line list comprehension:

import re

def foo(diff):
    return [tuple(re.search('\[(.+)\]', key).group(1).split('][')) for key in diff.keys()]
like image 3
Sebastian Lenartowicz Avatar answered Nov 08 '22 20:11

Sebastian Lenartowicz