Slice list to ordered chunks - python

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

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

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}

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

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.

Filtering out python dictionary based on a key`s values

I have a dictionary dictM in the form of
dictM={movieID:[rating1,rating2,rating3,rating4]}
Key is a movieID and rating1, rating2, rating3, rating4 are its values. There are several movieID's with ratings. I want to move certain movieID's along with ratings to a new dicitonary if a movieID has a certain number of ratings.
What I'm doing is :
for movie in dictM.keys():
if len(dictM[movie])>=5:
dF[movie]=d[movie]
But I'm not getting the desired result. Does someone know a solution for this?
You can use dictionary comprehension, as follows:
>>> dictM = {1: [1, 2, 3, 4], 2: [1, 2, 3]}
>>> {k: v for (k, v) in dictM.items() if len(v) ==4}
{1: [1, 2, 3, 4]}
You can try this using simple dictionary comprhension:
dictM={3:[4, 3, 2, 5, 1]}
new_dict = {a:b for a, b in dictM.items() if len(b) >= 5}
One reason why your code above may not be producing any results is first, you have not defined dF and the the length of the only value in dictM is equal to 4, but you want 5 or above, as shown in the if statement in your code.
You don't delete the entries, you could do it like this:
dictM = {1: [1, 2, 3],
2: [1, 2, 3, 4, 5],
3: [1, 2, 3, 4, 5, 6, 7],
4: [1]}
dF = {}
for movieID in list(dictM):
if len(dictM[movieID]) >= 5:
dF[movieID] = dictM[movieID] # put the movie + rating in the new dict
del dictM[movieID] # remove the movie from the old dict
The result looks like this:
>>> dictM
{1: [1, 2, 3], 4: [1]}
>>> dF
{2: [1, 2, 3, 4, 5], 3: [1, 2, 3, 4, 5, 6, 7]}

Categories

Resources