Removing items from list of dictionaries and appending them back - python

Suppose we have a list of dictionaries:
[{0: [0, 1, 2, 3], 1: [4]}, {2: [5, 6, 7, 8], 3: [9]}, {4: [10, 11, 12]}]
and we wish to split the dictionaries in this list according to some tuple for example (0, 5, 10) such that for each ith value in the tuple, if this value is present in any innermost sublist of the ith dictionary it gets split into its own list. In addition, the dictionaries are renumbered continuously from 0. Thus the output of the above would be.
[{0:[0], 1: [1, 2, 3], 2: [4]}, {3:[5], 4: [6, 7, 8], 5: [9]}, {6:[10], 7: [11, 12]}]
since 0 is a part of the first dictionary, it would get split. Since 5 is a part of the second dictionary, it would get split. Similarly, since 10 is the 3rd value in the tuple, and it is a part of the third dictionary it would get split.
I have written the following code:
for i in range(0, len(newlist)):
for key, value in newlist[i].items():
if x[i] in value:
value.remove(x[i])
newlist[i][key].append(x[i])
This produces [{0: [1, 2, 3, 0], 1: [4, 0]}, {2: [6, 7, 8, 5], 3: [9, 5]}, {4: [11, 12, 10]}] which is not the desired output, it appends to each list for the key.
How do I add just as a single list as in the desired output and how do I renumber the lists as desired using list comprehension or otherwise?

you can use a unified index idx to track the current key number in dict, and collect all the components splitted, and merge them into a new dict.
I have edit your example input to show more complex situation.
newlist = [{0: [0, 1, 2], 1: [3]}, {2: [4, 5, 6, 7, 8], 3: [9]}, {4: [10], 5:[11, 12]}]
x = [0, 5, 10]
for l in newlist:
components = []
for key, value in sorted(l.items()): # sort items because the storage of dict is unordered
for split_val in x:
if split_val in value: # split the list if in value
index = value.index(split_val)
components += [value[:index], [split_val], value[index + 1:]]
break
else:
components.append(value)
cur_dict = {}
for component in components:
if component: # only add non-empty component
cur_dict[idx] = component
idx += 1
result.append(cur_dict)
output:
[{0: [0], 1: [1, 2], 2: [3]}, {3: [4], 4: [5], 5: [6, 7, 8], 6: [9]}, {7: [10], 8: [11, 12]}]

I was able to obtain a combined dictionary using the following logic
li = [{0: [0, 1, 2, 3], 1: [4]}, {2: [5, 6, 7, 8], 3: [9]}, {4: [10, 11, 12]}]
values_list = []
#Iterate through values of all dictionaries and join them end to end
for dct in li:
values_list.extend(dct.values())
print(values_list)
#[[0, 1, 2, 3], [4], [5, 6, 7, 8], [9], [10, 11, 12]]
dct = {}
idx = 0
#Iterate through the list
for item in values_list:
#If we have more then one item in the list, idx goes to first item, idx+1 goes to rest, idx gets incremented by 2
if len(item) > 1:
dct[idx] = [item[0]]
dct[idx+1] = item[1:]
idx+=2
# If we have one item in the list, idx goes to first item, idx gets incremented by 1
else:
dct[idx] = [item[0]]
idx+=1
print(dct)
#{0: [0], 1: [1, 2, 3], 2: [4], 3: [5], 4: [6, 7, 8], 5: [9], 6: [10], 7: [11, 12]}

Related

Python - Check if value is unique in dictionary of lists

Is there a way to check if a value is unique for a specific list, within a dictionary of lists? If yes, I would like to know in which key/list pair/s the value is present.
For example:
{1: [3, 4, 5], 2: [4], 3: [5], 6: [3, 5, 9], 8: [3 ,8]}
I want to check if 3 is present in any of the other lists within the dictionary, besides 1: [3, 4, 5(compare it with all others). Since it is, then I must receive as a result 6 and 8, because these are the keys, which corresponding lists have this value.
Try this:
def find_n(nums, target, exception):
res = []
for n, nums_list in nums.items():
exception_key = [i for i in exception][0]
if n != exception_key and nums_list != exception[exception_key]:
if target in nums_list:
res.append(n)
return res
if __name__ == '__main__':
nums = {1: [3, 4, 5], 2: [4], 3: [5], 6: [3, 5, 9], 8: [3, 8]}
target = 3
exception = {1: [3, 4, 5]}
print(find_n(nums, target, exception))
Reuslt:
[6, 8]
You can enter the value you are looking for and then loop over all the dictionaries to see if its in them.
Take this as pseudo code:
int val = 3
for(keys in mydict):
if(mydict.key.contains(val())
print(mydict.key)
You could also save the dictionaries in a list, so that you have a list of dicts with your corresponding value.
Not entirely sure if this is what you meant but:
def unique(list1):
# initialize a null list:
unique_list = []
# traverse for all elements:
for x in list1:
# check if exists in unique_list or not
if x not in unique_list:
unique_list.append(x)
# printing the list:
for x in unique_list:
print(x, end=' ')
list1 = [10, 20, 10, 30, 40, 40]
print("the unique values from 1st list is")
unique(list1)
list2 = [1, 2, 1, 1, 3, 4, 3, 3, 5]
print("\nthe unique values from 2nd list is")
unique(list2)
This code will loops through all the values and ensure that they are not repeated.
You could write some simple helper functions like so:
my_dict = {1: [3, 4, 5], 2: [4], 3: [5], 6: [3, 5, 9], 8: [3 ,8]}
def keys_containing_element(dict_of_lists: dict, element: int) -> list:
"""Return all keys in dict_of_list whose list contains element"""
return [key for key in dict_of_lists.keys() if element in dict_of_lists[key]]
def is_element_unique(dict_of_lists: dict, element: int) -> list:
"""Return true if element is in exactly one of the lists in dict_of_lists"""
return True if len(keys_containing_element(dict_of_lists, element)) == 1 else False
Now,
print(keys_containing_element(my_dict, 3))
print(is_element_unique(my_dict, 3))
print(keys_containing_element(my_dict, 9))
print(is_element_unique(my_dict, 9))
outputs
[1, 6, 8]
False
[6]
True

Match dictionary keys and values in new dictionary

I trying to match keys and values in a dictionary together and put them into a new dictionary.
My initial dictionary looks like this:
d = {1: [5,6], 3: [4], 8: [2,3]}
I know that I can access keys and values using d.keys() and d.values().
My goal is to find all items which relate. I think it is best explained if I illustrate my desired output.
I wish to create a new dict that gives me:
finaldict = {1: [5,6], 2: [3,4,8], 3: [2,4,8], 4:[2,3,8] 5:[1,6], 6:[1,5], 8:[2,3,4]}
That is, I want to get keys of all numbers, and give values to what number they are related to.
My attempt:
d = {1: [5,6], 3:[4],8:[2,3]}
print(d)
keys = list(d.keys())
vals = list(d.values())
for i in range(0,len(d.keys())):
current_vals = list(vals[i])
length = len(current_vals)
for v in current_vals:
if v in d.keys(): #if the dictionary exists, then append
add = [keys[i],current_vals]
d[v].append(add)
else:
add2 = [keys[i],current_vals]
empty = []
for a in add2:
if type(a) == int:
empty.append(a)
if type(a) == list:
for b in a:
empty.append(b)
d[v] = empty
keys = list(d.keys())
vals = list(d.values())
for i in range(0,len(keys)):
if keys[i] in vals[i]:
vals[i].remove(keys[i])
finaldict = {}
for j in range(0,len(keys)):
finaldict[keys[j]] = vals[j]
print("Final dict:\n",finaldict)
My attempt gives me the output
Final dict:
{1: [5, 6], 3: [4, [8, [2, 3]]], 8: [2, 3], 5: [1, 6], 6: [1, 5], 4: [3], 2: [8, 3]}
As you can see, finaldict[3] has the values [4, [8, [2, 3,]]].
The values themselves are wrong (3 should not be there), and also I want this to be a single list, and not on the format it is now. There are also other issues, such as finaldict[2] having the values [8, 3] when it should, in fact, have the values [3, 4, 8] and finaldict[4] only having [3], when it should have [2, 3, 8].
Not the most beautiful code I've ever written, but here it goes.
I create sets with all the numbers that are related to each other, then create a dictionary of each of those numbers as key, with the other ones as values.
d = {1: [5,6], 3: [4], 8: [2,3]}
pools = []
for key,values in d.items():
for pool in pools:
if key in pool or any(val in pool for val in values):
pool.add(key)
[pool.add(val) for val in values]
break
else:
pools.append(set((key, *values)))
#pools = [{1, 5, 6}, {8, 2, 3, 4}]
finaldict = {k:set.difference(v, set((k,))) for pool in pools for k, v in zip(pool, [pool]*len(pool))}
print(finaldict)
You can use recursion:
d = {1: [5,6], 3: [4], 8: [2,3]}
def groups(v, seen = [], c = []):
if (k:=[i for a, b in d.items() for i in ({a, *b} if v in {a, *b} else []) if i not in {*seen, v}]):
for i in k:
yield from groups(i, seen=seen+[v], c = c+([v] if seen else []))
else:
yield c+[v]
r = {i:[*{j for k in groups(i) for j in k}] for a, b in d.items() for i in [a, *b]}
Output:
{1: [5, 6], 5: [1, 6], 6: [1, 5], 3: [8, 2, 4], 4: [8, 2, 3], 8: [2, 3, 4], 2: [8, 3, 4]}
You can build up the resulting dictionary by merging related groups and assigning the same merged group to every key that is part of it:
d = {1: [5,6], 3: [4], 8: [2,3]}
related = dict()
for key,group in d.items(): # merge/assign groups to keys
merged = set(group).union({key},*(related.get(k,[]) for k in group))
related.update((k,merged) for k in merged)
related = {k:list(g-{k}) for k,g in related.items()} # exclude key from group
print(related)
{1: [5, 6], 5: [1, 6], 6: [1, 5], 3: [8, 2, 4], 4: [8, 2, 3],
2: [8, 3, 4], 8: [2, 3, 4]}

Adding integers inside list values within a dictionary

Given:
a_dict = {3: [3, 7], 7: [6, 2], 2: [2, 1], 8: [-3, 9]}
I'm stuck on a problem where I have to return an updated dictionary that contains the sum of the elements inside the list values alongside its key.
For example, the first key pair:
3: [3, 7]
is to be turned into this:
3: 10
Returned updated dictionary should look like this:
{3: 10, 7: 8, 2: 3, 8: 6}
I'm not sure if I should use the values method to isolate the values to get the sum of numbers from the list values.
for key, val in a_dict.items():
a_dict[key] = sum(val)
Use a dict comprehension:
{k: sum(v) for k, v in a_dict.items()}
# {3: 10, 7: 8, 2: 3, 8: 6}

Collapsing dictionary by merging matching keys and key,value pairs

So I am trying to find a way to "merge" a dependency list which is in the form of a dictionary in python, and I haven't been able to come up with a solution. So imagine a graph along the lines of this: (all of the lines are downward pointing arrows in this directed graph)
1 2 4
\ / / \
3 5 8
\ / \ \
6 7 9
this graph would produce a dependency dictionary that looks like this:
{3:[1,2], 5:[4], 6:[3,5], 7:[5], 8:[4], 9:[8], 1:[], 2:[], 4:[]}
such that keys are nodes in the graph, and their values are the nodes they are dependent on.
I am trying to convert this into a total ancestry list in terms of a tree, so that each node is a key, and its value is a list of ALL nodes that lead to it, not just it's immediate parents. The resulting dictionary would be:
{3:[1,2], 5:[4], 6:[3, 5, 1, 2, 4], 7:[5, 4], 8:[4], 9:[8, 4], 1:[], 2:[], 3:[]}
Any suggestions on how to solve this? I have been banging my head into it for a while, tried a recursive solution that I haven't been able to get working.
You can use a chained dict comprehension with list comprehension for up to two nodes.
>>> {k: v + [item for i in v for item in d.get(i, [])] for k,v in d.items()}
{3: [1, 2],
5: [4],
6: [3, 5, 1, 2, 4],
7: [5, 4],
8: [4],
9: [8, 4],
1: [],
2: [],
4: []}
For unlimited depth, you can use a recursive approach
def get_ant(node, d):
if node:
return d.get(node,[]) + [item for x in d.get(node, []) for item in get_ant(x, d) ]
return []
Then,
>>> get_ant(6, d)
[3, 5, 1, 2, 10, 4]
To get all cases:
>>> {k: get_ant(k, d) for k in d.keys()}
{3: [1, 2, 10],
5: [4],
6: [3, 5, 1, 2, 10, 4],
7: [5, 4],
8: [4],
9: [8, 4],
1: [10],
2: [],
4: []}
Here's a really simple way to do it.
In [22]: a
Out[22]: {1: [], 2: [], 3: [1, 2], 4: [], 5: [4], 6: [3, 5], 7: [5], 8: [4], 9: [8]}
In [23]: final = {}
In [24]: for key in a:
...: nodes = set()
...:
...: for val in a[key]:
...: nodes.add(val)
...: if val in a:
...: nodes.update(set(a[val]))
...:
...: final[key] = list(nodes)
In [25]: final
Out[25]:
{1: [],
2: [],
3: [1, 2],
4: [],
5: [4],
6: [3, 1, 2, 5, 4],
7: [5, 4],
8: [4],
9: [8, 4]}

Slice list to ordered chunks

I have dictionary like:
item_count_per_section = {1: 3, 2: 5, 3: 2, 4: 2}
And total count of items retrieved from this dictionary:
total_items = range(sum(item_count_per_section.values()))
Now I want to transform total_items by values of dictionary following way:
items_no_per_section = {1: [0,1,2], 2: [3,4,5,6,7], 3:[8,9], 4:[10,11] }
I.e. slice total_items sequencially to sublists which startrs from previous "iteration" index and finished with value from initial dictionary.
You don't need to find total_items at all. You can straightaway use itertools.count, itertools.islice and dictionary comprehension, like this
from itertools import count, islice
item_count_per_section, counter = {1: 3, 2: 5, 3: 2, 4: 2}, count()
print {k:list(islice(counter, v)) for k, v in item_count_per_section.items()}
Output
{1: [0, 1, 2], 2: [3, 4, 5, 6, 7], 3: [8, 9], 4: [10, 11]}
dict comprehension of itertools.isliced iter of total_items:
from itertools import islice
item_count_per_section = {1: 3, 2: 5, 3: 2, 4: 2}
total_items = range(sum(item_count_per_section.values()))
i = iter(total_items)
{key: list(islice(i, value)) for key, value in item_count_per_section.items()}
Outputs:
{1: [0, 1, 2], 2: [3, 4, 5, 6, 7], 3: [8, 9], 4: [10, 11]}
Note: this works for any total_items, not just range(sum(values)), assuming that was just your sample to keep the question generic. If you do just want the numbers, go with #thefourtheye's answer

Categories

Resources