Combine Python dictionaries that have the same Key name - python

I have two separate Python List that have common key names in their respective dictionary. The second list called recordList has multiple dictionaries with the same key name that I want to append the first list clientList. Here are examples lists:
clientList = [{'client1': ['c1','f1']}, {'client2': ['c2','f2']}]
recordList = [{'client1': {'rec_1':['t1','s1']}}, {'client1': {'rec_2':['t2','s2']}}]
So the end result would be something like this so the records are now in a new list of multiple dictionaries within the clientList.
clientList = [{'client1': [['c1','f1'], [{'rec_1':['t1','s1']},{'rec_2':['t2','s2']}]]}, {'client2': [['c2','f2']]}]
Seems simple enough but I'm struggling to find a way to iterate both of these dictionaries using variables to find where they match.

When you are sure, that the key names are equal in both dictionaries:
clientlist = dict([(k, [clientList[k], recordlist[k]]) for k in clientList])
like here:
>>> a = {1:1,2:2,3:3}
>>> b = {1:11,2:12,3:13}
>>> c = dict([(k,[a[k],b[k]]) for k in a])
>>> c
{1: [1, 11], 2: [2, 12], 3: [3, 13]}

Assuming you want a list of values that correspond to each key in the two lists, try this as a start:
from pprint import pprint
clientList = [{'client1': ['c1','f1']}, {'client2': ['c2','f2']}]
recordList = [{'client1': {'rec_1':['t1','s1']}}, {'client1': {'rec_2':['t2','s2']}}]
clientList.extend(recordList)
outputList = {}
for rec in clientList:
k = rec.keys()[0]
v = rec.values()[0]
if k in outputList:
outputList[k].append(v)
else:
outputList[k] = [v,]
pprint(outputList)
It will produce this:
{'client1': [['c1', 'f1'], {'rec_1': ['t1', 's1']}, {'rec_2': ['t2', 's2']}],
'client2': [['c2', 'f2']]}

This could work but I am not sure I understand the rules of your data structure.
# join all the dicts for better lookup and update
clientDict = {}
for d in clientList:
for k, v in d.items():
clientDict[k] = clientDict.get(k, []) + v
recordDict = {}
for d in recordList:
for k, v in d.items():
recordDict[k] = recordDict.get(k, []) + [v]
for k, v in recordDict.items():
clientDict[k] = [clientDict[k]] + v
# I don't know why you need a list of one-key dicts but here it is
clientList = [dict([(k, v)]) for k, v in clientDict.items()]
With the sample data you provided this gives the result you wanted, hope it helps.

Related

Reformating a dictionary of list based on items in another list

I have a dictionary of lists like
source = {"name":["hans","james","mat"],"country":["spain"],"language":["english","french"]}
and another list like
data_not_avail = ["hans","spain","mat"]
How is it possible to reformat source dictionary into the following format
{
"exist":{"name":["james"], "language":["english","french"]},
"not_exist":{"name":["hans","mat"], "country":["spain"]}
}
I was trying to solve by finding the key of item which are present in list but it was not a success
data_result = {}
keys_list = []
for v in data_not_avail:
keys = [key for key, value in source.items() if v in value]
data_result.update({keys[0]:[v]})
keys_list.extend(keys)
This is a approach, you can use a list comprehension (or python built in filter) to filter every element within source lists with the content of data_not_avail.
data = {"exist": {}, "not_exist": {}}
for key, value in source.items():
data["exist"][key] = [v for v in value if v not in data_not_avail]
data["not_exist"][key] = [v for v in value if v in data_not_avail]
# if you dont need empty list in the result
if not data["exist"][key]:
del data["exist"][key]
if not data["not_exist"][key]:
del data["not_exist"][key]
Naive way of solving it is this, check it out.
values = list(source.values())
exist_values = []
not_values = []
for l in values:
temp_exist = []
temp_not = []
for item in l:
if item not in data_not_avail:
temp_exist.append(item)
else:
temp_not.append(item)
exist_values.append(temp_exist)
not_values.append(temp_not)
exist = {}
not_exist = {}
keys = ['name', 'language', 'country']
for i,key in enumerate(keys):
if len(exist_values[i]) != 0:
exist[key] = exist_values[i]
if len(not_values[i]) != 0:
not_exist[key] = not_values[i]
print(exist, not_exist)
#{'name': ['james'], 'country': ['english', 'french']}
#{'name': ['hans', 'mat'], 'language': ['spain']}

Split list of strings based on occurrence count

I have list of strings as input
str_list1 = ['e1:g1', 'e2:g1', 'e1:o1', 'e2:o1']
str_list2 = ['e1:o1', 'e2:g1']
expected outputs
str_list1_output = [e1, e2] [g1, o1]
str_list2_output = [e1] [o1] [e2] [g1]
I wrote below code but it is failing for str_list2
str_list1 = ['e1:g1', 'e2:g1', 'e1:o1', 'e2:o1']
str_list2 = ['e1:o1', 'e2:g1']
entities, properties = zip(*(s.split(":") for s in str_list1))
print(set(entities)) # {'e1', 'e2'}
print(set(properties)) # {'g1', 'o1'}
I have 2 questions:-
What is wrong in the code? Above code returning same output for str_list2 as well instead of 4 separate list "[e1] [o1] [e2] [g1]"
Can I use zip to generate 2 separate sets of entities and properties instead of converting list to set while printing it?
Not sure if this is an exact answer to your question (namely, why the code you have tried is not giving the result you want), but I believe the code below will give the desired result, and perhaps the insight needed to answer your question can be found here:
str_list1 = ['e1:g1', 'e2:g1', 'e1:o1', 'e2:o1']
str_list2 = ['e1:o1', 'e2:g1']
str_list3 = ['e1:g1', 'e1:o1', 'e2:o1']
def foo(str_list):
from collections import defaultdict
dct = defaultdict(list)
for s in str_list:
entity, property = s.split(":")
dct[entity] += [property]
for k in dct:
dct[k] = tuple(dct[k])
rdct = defaultdict(list)
for k, v in dct.items():
rdct[v] += [k]
L = []
for k, v in rdct.items():
L += [v, list(k)]
print(L)
foo(str_list1)
foo(str_list2)
foo(str_list3)
Output:
[['e1', 'e2'], ['g1', 'o1']]
[['e1'], ['o1'], ['e2'], ['g1']]
[['e1'], ['g1', 'o1'], ['e2'], ['o1']]

How can I update all entries of a dictionary in Python with certain logic?

I have a dictionary with key value pairs of a persons name with a list of domain names like so
dictionary = {
'Trent':['help.google.com', 'smooth.google.com', 'bob.google.com'],
'Bill':['help.google.com', 'smooth.google.com', 'bob.google.com', 'trent.awesome.net']}
I want to make it so that in the dictionary, there is only the parent domain (instead of smooth.google.com it's just google.com). Ordinarily, with a regular list I'll use code like this get the parent domain names:
domains = ['help.google.com', 'smooth.google.com', 'bob.google.com', 'trent.awesome.net']
parents = []
for domain in domains:
parents.append(domain[domain.index('.') + 1:])
Now I'm trying to combine that logic with logic that makes sure that in the dictionary, among values no matter the key, there are no duplicates using Counter and list comprehension. That code is this:
cnt = Counter()
for idx in result.values():
cnt.update(idx)
res = {idx: [key for key in j if cnt[key] == 1]
for idx, j in result.items()}
When I try to combine the logic, the BEST I'll get is an empty list next to the name. Using the above example of a dictionary the result will be
'Trent':[]
I tried using two for loops like so:
cnt = Counter()
for idx in result.values():
for x in idx:
x = x[x.index('.') + 1:]
cnt.update(idx)
res = {idx: [key for key in j if cnt[key] == 1]
for idx, j in result.items()}
Any help is greatly appreciated. I hope I've provided sufficient detail in my question.
This script will filter out domains in the list and keeps only parent domains:
dictionary = {
'Trent':['help.google.com', 'smooth.google.com', 'bob.google.com'],
'Bill':['help.google.com', 'smooth.google.com', 'bob.google.com', 'trent.awesome.net']}
out = {k: [*set(vv.split('.', maxsplit=1)[-1] for vv in v)] for k, v in dictionary.items()}
print(out)
Prints:
{'Trent': ['google.com'], 'Bill': ['google.com', 'awesome.net']}
EDIT: To filter out the duplicities across every key, you can use this:
out, seen = {}, set()
for k, v in dictionary.items():
for vv in v:
domain = vv.split('.', maxsplit=1)[-1]
if domain not in seen:
out.setdefault(k, []).append(domain)
seen.add(domain)
print(out)
Prints:
{'Trent': ['google.com'], 'Bill': ['awesome.net']}

Get specific key of a nested iterable and check if its value exists in a list

I am trying to access a specific key in a nest dictionary, then match its value to a string in a list. If the string in the list contains the string in the dictionary value, I want to override the dictionary value with the list value. below is an example.
my_list = ['string1~', 'string2~', 'string3~', 'string4~', 'string5~', 'string6~']
my_iterable = {'A':'xyz',
'B':'string6',
'C':[{'B':'string4', 'D':'123'}],
'E':[{'F':'321', 'B':'string1'}],
'G':'jkl'
}
The key I'm looking for is B, the objective is to override string6 with string6~, string4 with string4~, and so on for all B keys found in the my_iterable.
I have written a function to compute the Levenshtein distance between two strings, but I am struggling to write an efficient ways to override the values of the keys.
def find_and_replace(key, dictionary, original_list):
for k, v in dictionary.items():
if k == key:
#function to check if original_list item contains v
yield v
elif isinstance(v, dict):
for result in find_and_replace(key, v, name_list):
yield result
elif isinstance(v, list):
for d in v:
if isinstance(d, dict):
for result in find_and_replace(key, d, name_list):
yield result
if I call
updated_dict = find_and_replace('B', my_iterable, my_list)
I want updated_dict to return the below:
{'A':'xyz',
'B':'string6~',
'C':[{'B':'string4~', 'D':'123'}],
'E':[{'F':'321', 'B':'string1~'}],
'G':'jkl'
}
Is this the right approach to the most efficient solution, and how can I modify it to return a dictionary with the updated values for B?
You can use below code. I have assumed the structure of input dict to be same throughout the execution.
# Input List
my_list = ['string1~', 'string2~', 'string3~', 'string4~', 'string5~', 'string6~']
# Input Dict
# Removed duplicate key "B" from the dict
my_iterable = {'A':'xyz',
'B':'string6',
'C':[{'B':'string4', 'D':'123'}],
'E':[{'F':'321', 'B':'string1'}],
'G':'jkl',
}
# setting search key
search_key = "B"
# Main code
for i, v in my_iterable.items():
if i == search_key:
if not isinstance(v,list):
search_in_list = [i for i in my_list if v in i]
if search_in_list:
my_iterable[i] = search_in_list[0]
else:
try:
for j, k in v[0].items():
if j == search_key:
search_in_list = [l for l in my_list if k in l]
if search_in_list:
v[0][j] = search_in_list[0]
except:
continue
# print output
print (my_iterable)
# Result -> {'A': 'xyz', 'B': 'string6~', 'C': [{'B': 'string4~', 'D': '123'}], 'E': [{'F': '321', 'B': 'string1~'}], 'G': 'jkl'}
Above can has scope of optimization using list comprehension or using
a function
I hope this helps and counts!
In some cases, if your nesting is kind of complex you can treat the dictionary like a json string and do all sorts of replacements. Its probably not what people would call very pythonic, but gives you a little more flexibility.
import re, json
my_list = ['string1~', 'string2~', 'string3~', 'string4~', 'string5~', 'string6~']
my_iterable = {'A':'xyz',
'B':'string6',
'C':[{'B':'string4', 'D':'123'}],
'E':[{'F':'321', 'B':'string1'}],
'G':'jkl'}
json_str = json.dumps(my_iterable, ensure_ascii=False)
for val in my_list:
json_str = re.sub(re.compile(f"""("[B]":\\W?")({val[:-1]})(")"""), r"\1" + val + r"\3", json_str)
my_iterable = json.loads(json_str)
print(my_iterable)

Change Keys in dictionary

I have a dictionary:
d = {1:[9,9,9],2:[8,8,8],3:[7,7,7]}
and a list of keys :
newkeylist = [4,2,3]
Now i want check the keys in the dict with the content in the list. If they are different i want to replace the key in the dict with the one in the list.
for i in range(len(newkeylist)):
if d.key()[i] != newkeylist[i]:
d.key()[i] = newkeylist[i]
try something like this
d = {1:[9,9,9],2:[8,8,8],3:[7,7,7]}
newkeylist = [4,2,3]
d_copy = d.copy()
for i, (k, v) in enumerate(d_copy.items()):
if k != newkeylist[i]:
d[newkeylist[i]] = v
del d[k]
but as #jonrsharpe said, it's not an ordered dict: the output is random

Categories

Resources