4

I've two lists containing dictionaries. I want to compare certain fields in each of these dictionaries.

current_list = [{"name": "Bill","address": "Home", "age": 23, "accesstime":11:14:01}, 
            {"name": "Fred","address": "Home", "age": 26, "accesstime":11:57:43},
            {"name": "Nora","address": "Home", "age": 33, "accesstime":11:24:14}]

backup_list = [{"name": "Bill","address": "Home", "age": 23, "accesstime":13:34:24}, 
           {"name": "Fred","address": "Home", "age": 26, "accesstime":13:34:26},
           {"name": "Nora","address": "Home", "age": 33, "accesstime":13:35:14}]

The list / dictionaries should be the same in order, and i just want to compare certain key, value pairs. Like name, address, age and ignore access time, but what i have so far compares each key / pair. So i just want to compare

current_list:dictionary[0][name] -> backup_list:dictionary[0][name] and then 
current_list:dictionary[0][address] -> backup_list:dictionary[0][address] 

and so on.

for x in current_list:
    for y in backup_list:
        for k, v in x.items():
            for kk, vv in y.items():
                if k == kk:
                    print("Match: {0}".format(kk))
                    break
                elif k != kk:
                    print("No match: {0}".format(kk))

Current output

Match name with name
No Match address with name
Match address with address
No Match age with name
No Match age with address
Match age with age
No Match dateRegistered with name
No Match dateRegistered with address
No Match dateRegistered with age
Match dateRegistered with dateRegistered

Preferred output

Match name with name
Match address with address
Match age with age

* Due to a requirement change my list became a list of Elementtree xml elements *

So instead of the above list, its becomes

backup_list =  ["<Element 'New' at 0x0000000002698C28>, <Element 'Update' at 0x0000000002698CC8>, <Element 'New' at 0x0000000002698CC8>"]

Where the ElementTree is an xml element containing:

{"name": "Nora", "address": "Home", "age": 33, "dateRegistered": 20140812}"

So this based on the answer below seems to satisfy my requirements so far:

value_to_compare = ["name", "address", "age"]
for i, elem in enumerate(current_list):
    backup_dict = backup_list[i]
    if elem.tag == "New":
        for key in value_to_compare:
            try:
                print("Match {0} {1} == {2}:".format(key, backup_dict.attrib[key], elem.attrib[key]))
            except KeyError:
                print("key {} not found".format(key))
            except:
                raise
    else:
        continue
John
  • 787
  • 4
  • 11
  • 28
  • I've just found out i can't use a list of dictionaries as there is some other criteria i have to account for. So its actually got to be a list of xml elements.e.g. [, , ] . RomainL's solution is close to what i need. – John Nov 21 '18 at 14:49

7 Answers7

2

I don't know if I fully understood your question but I think the following code should do the trick:

compare_arguments = ["name", "age", "address"]
for cl, bl in zip(current_list, backup_list):
    for ca in compare_arguments:
        if cl[ca] == bl[ca]:
            print("Match {0} with {0}".format(cl[ca]))
    print("-" * 10)

What is done in the code above is a zip iteration over both lists. With another list you specify the fields you want to compare. In the main loop you iterate over the comparable fields and print them accordingly.

Woltan
  • 13,723
  • 15
  • 78
  • 104
  • you have to add `"address"` in your `compare_arguments` list – Ma0 Nov 21 '18 at 12:16
  • @Ev.Kounis I thought that the question was about comparing only a subset of the possible dictionary entries. This is why I explicitly define the keys on which the comparison is done. Thank you for the hint... – Woltan Nov 21 '18 at 13:06
2

Someone has already made a module called deepdiff that does this and sooo much more! Refer to this answer for their detailed explanation!

First - install it

pip install deepdiff

Then - enjoy

#of course import it
from deepdiff import DeepDiff

current_list, backup_list = [...], [...] #values stated in question.

for c, b in zip(current_list, backup_list):
    dif = DeepDiff(c, b)
    for key in ["name", "age", "address"]:
        try:
            assert dif['values_changed'][f"root['{key}'"]
            #pass the below line to exclude any non-matching values like your desired output has
            print(f"No Match {key} with {key}")
        except KeyError:
            print(f"Match {key} with {key}")

Results: - as expected

Match name with name
Match address with address
Match age with age
Match name with name
Match address with address
Match age with age
Match name with name
Match address with address
Match age with age

Final Note

This module has soo much else you can utilize such as type changes, key changes/removals/additions, an extensive text comparison, and searches as well. Definitely well worth a look into.

~GL on your project!

Jab
  • 26,853
  • 21
  • 75
  • 114
1

Simply compare with this-

for current in current_list:
    for backup in backup_list:
        for a in backup:
            for b in current:
                if a == b:
                    if a == "name" or a== "age" or a== "address" :
                        if backup[a] == current[b]:
                            print (backup[a])
                            print (current[b])
vipul gangwar
  • 313
  • 1
  • 4
  • 16
0

You can compare all corresponding fields with this code:

for dct1, dct2 in zip(current_list, backup_list):
    for k, v in dct1.items():
        if k == "accesstime":
            continue
        if v == dct2[k]:
            print("Match: {0} with {0}".format(k))
        else:
            print("No match: {0} with {0}".format(k))

Note that the values of your "accesstime" keys are not valid Python objects!

Ma0
  • 15,057
  • 4
  • 35
  • 65
0

I do not understand the rationnal of your data structure, but I think that will do the trick:

value_to_compare = ["name", "address", "age"]

for i, elem in enumerate(current_list):
    backup_dict = backup_list[i]
    for key in value_to_compare:
        try:
            print("Match {}: {} with {}".format(key, elem[key], backup_dict[key]))
        except KeyError:
            print("key {} not found".format(key))
            # may be a raise here.
        except:
            raise
RomainL.
  • 997
  • 1
  • 10
  • 24
  • I've just found out i can't use a list of dictionaries as there is some other criteria i have to account for. So its actually got to be a list of xml elements.e.g. [, , ] – John Nov 21 '18 at 14:48
  • Should this be print("Match {}: {} with {}".format(key, elem[i], backup_dict[i])) because key is a value from value_to_compare. Where i will be an integer? – John Nov 21 '18 at 15:53
  • if I understand, you will want to compare the xml element? but only on some fields? I am not sure to understand you new data can you make an update to your question? or ask a new one? – RomainL. Nov 21 '18 at 16:18
  • Updated my answer above based on your solution. Thanks :) – John Nov 21 '18 at 16:38
0

If you are happy to use a 3rd party library, this kind of task can be more efficiently implemented, and in a more structured way, via Pandas:

import pandas as pd

res = pd.merge(pd.DataFrame(current_list),
               pd.DataFrame(backup_list),
               on=['name', 'address', 'age'],
               how='outer',
               indicator=True)

print(res)

  accesstime_x address  age  name accesstime_y _merge
0     11:14:01    Home   23  Bill     13:34:24   both
1     11:57:43    Home   26  Fred     13:34:26   both
2     11:24:14    Home   33  Nora     13:35:14   both

The result _merge = 'both' for each row indicates the combination of ['name', 'address', 'age'] occurs in both lists but, in addition, you get to see the accesstime from each input.

jpp
  • 159,742
  • 34
  • 281
  • 339
0

You can use zip method to iterate over lists simultaneously.

elements_to_compare = ["name", "age", "address"]
for dic1, dic2 in zip(current_list, backup_list):
    for element in elements_to_compare :
        if dic1[element] == dic2[element]:
            print("Match {0} with {0}".format(element))