Adding integers inside list values within a dictionary - python

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}

Related

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]}

Removing items from list of dictionaries and appending them back

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]}

How to search throughout the first cell of lists, with each list being values in a dict?

I am working on a dictionary containing keys and values, with the keys being IDs and each value being a list. How could I check a condition like, "if there is 6 as the FIRST element of any list"?
dict_ = {0: [1, 2, 4, 1], 1: [3, 8, 4, 7], 2: [6, 2, 4, 2], 3: [5, 1, 3, 6]}
if 6 in dict_.values[0]():
print("6 is in the first cell of one of the lists")
return(True)
This looks like what I want to do but it's not the right syntax and gives me "TypeError object is not sub scriptable". I expect it to return True only if there is a 6 on the [0] of one of the lists.
You can use the built-in function any for a one-liner:
any(val[0] == 6 for val in dict.values())
Add an if val at the end for extra safety, in case any of the lists are null or empty.
Answer:
dictionary = {0: [1, 2, 4, 1], 1: [3, 8, 4, 7], 2: [6, 2, 4, 2], 3: [5, 1, 3, 6]}
for key, val in dictionary.items():
if val[0] == 6:
print("6 is present at 1 st position :", key,":",val)
Output:
6 is present at 1 st position : 2 : [6, 2, 4, 2]
First, never shadow a built-in: use d or dict_ instead of dict as a variable name. Second, note that dict.values() returns a view, you can't apply __getitem__ (or []) on a view object, let alone a method which you haven't called via parentheses.
You can modify your function and use simple iteration:
d = {0: [1, 2, 4, 1], 1: [3, 8, 4, 7], 2: [6, 2, 4, 2], 3: [5, 1, 3, 6]}
def check_value(d, val, pos=0):
for value in d.values():
if value[pos] == val:
return True
return False
res = check_value(d, 6) # True
You can also use any with a generator expression:
def check_value_any(d, val, pos=0):
return any(value[pos] == val for value in d.values())
This is lazy, i.e. if the conditional is met at any point any returns True and the function returns True. However, this may be less efficient as generators carry additional overhead.
I'm not sure what you mean but I'll try to answer you.
First of all, you iterate through dictionary like this:
for key, values in d.items():
you can also use d.keys() or d.values(), depending on what you need.
Now, if you need to see if 6 is on the first position in any list in dictionary, you can do it like this:
for key, value in d.items():
if value[0] == 6:
return True
If you want to see if number 6 appears in the first list (of the first key), you can do that with:
for value in list(d.keys())[0]:
if value == 6:
return True
Let's get the values of the dict first:
dict_ = {0: [1, 2, 4, 1], 1: [3, 8, 4, 7], 2: [6, 2, 4, 2], 3: [5, 1, 3, 6]}
for key, val in dict_.items():
print(val)
OUTPUT:
[1, 2, 4, 1]
[3, 8, 4, 7]
[6, 2, 4, 2]
[5, 1, 3, 6]
Now to check if the values (list) has a 6 on the first index:
for key, val in dict_.items(): # for each key, val (list) in the dict
if val[0] == 6: # [0] for the first index of each list
print("6 found at the first index in the list: ", val)
OUTPUT:
6 found at the first index in the list: [6, 2, 4, 2]

pythonic way of reassigning values to dictionary

I have a dictionary with lists as values:
my_dict = {1: [2,3], 2: [4, 5], 3: [6, 7]}
and I want to get to update the dictionary to update the values to be the sum of the old list values:
my_dict = {1: 5, 2: 9, 3: 13}
What is the most efficient/pythonic way of doing so? What I usually do is:
for key in my_dict:
my_dict[key] = sum(my_dict[key])
Are there better ways?
You can use a dictionary comprehension:
my_dict = {1: [2,3], 2: [4, 5], 3: [6, 7]}
new_d = {a:sum(b) for a, b in my_dict.items()}
Output:
{1: 5, 2: 9, 3: 13}
You can use reduce instead of sum:
from functools import reduce
my_dict = {1: [2,3], 2: [4, 5], 3: [6, 7]}
final = {k: reduce(lambda x,y: x+y, v) for k,v in my_dict.items()}
output:
{1: 5, 2: 9, 3: 13}
Otherwise you can refer to this thread for more informations.

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