Python, List append in recursion - python

rows = []
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
rows.append(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(diry)
rec(diry,level+1)
rec({},0)
print(rows)
The above code prints
[{'column_0': 0, 'column_1': 1, 'column_2': 2},
{'column_0': 0, 'column_1': 1, 'column_2': 2},
{'column_0': 0, 'column_1': 1, 'column_2': 2}]
Shouldn't it print
[{'column_0': 0},
{'column_0': 0, 'column_1': 1},
{'column_0': 0, 'column_1': 1, 'column_2': 2}]
Any suggestions will be helpful. Thanks

It is something distracting for beginners. You have to make sure that you pass a copy of dictionary or else it is the same reference to dictionary that you end up modifying:
rec(diry.copy(), level+1)
Code:
rows = []
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
rows.append(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(diry)
rec(diry.copy(),level+1) # CHANGE HERE
rec({},0)
print(rows)

I am going from top to bottom but i think it is what you want:
def rec_append(n):
if n == 0:
return [{f'col{n}': n}]
else:
return (rec_append(n-1)) + [{f'col{n}': n for n in range(n+1)}]
print(rec_append(3))
[{'col0': 0},
{'col0': 0, 'col1': 1},
{'col0': 0, 'col1': 1, 'col2': 2},
{'col0': 0, 'col1': 1, 'col2': 2, 'col3': 3}]

You are changing the dictionary in your iterations and inserting a reference to it in the list. At the end you have inserted three times the reference to the same dictionary and therefore the last print is just three printing outputs of the same dictionary.
If you really want to have each time a reference to the dictionary at that time you have to make a copy:
rows = []
import copy
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
rows.append(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(copy.deepcopy(diry))
rec(diry,level+1)
rec({},0)
print(rows)
Output:
[{'column_0': 0}, {'column_0': 0, 'column_1': 1}, {'column_0': 0, 'column_1': 1, 'column_2': 2}]

You can change your code to this to get the desired output:-
rows = []
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
# print(diry)
rows.append(diry)
print(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(diry)
print(diry)
rec(diry,level+1)
rec({},0)
Output:-
{'column_0': 0}
{'column_0': 0, 'column_1': 1}
{'column_0': 0, 'column_1': 1, 'column_2': 2}
Now you can compare this code and your code to get the intuition of why you are getting the result you were thinking.

Related

How to create a dictionary of permutation values?

I need to assign 0 and 1 as values to keys in the dictionaries:
combinations_string_list = [num_list_to_str(i) for i in itertools.product([0, 1], repeat=2)]
all_stategy = []
for i in range(16):
strategy_table = {x: y for x in combinations_string_list for y in [0, 1]}
all_stategy.append(strategy_table)
print(all_stategy)
I got [{'00': 1, '01': 1, '10': 1, '11': 1}, {'00': 1, '01': 1, '10': 1, '11': 1}, {'00': 1, '01': 1, '10': 1, '11': 1}, ...]
but I need [{'00': 0, '01': 0, '10': 0, '11': 0}, {'00': 0, '01': 0, '10': 0, '11': 1}, {'00': 0, '01': 0, '10': 1, '11': 0}, ...] instead.
How can I create this kind of value? Thanks!
You can zip the key sequence ["0", "1"] with each element of the cartesian product to produce input to dict:
>>> [dict(zip(["0", "1"], x)) for x in product([0,1], repeat=2)]
[{'0': 0, '1': 0}, {'0': 0, '1': 1}, {'0': 1, '1': 0}, {'0': 1, '1': 1}]
or
>>> values=[0,1]
>>> [dict(zip(map(str, values), x)) for x in product(values, repeat=2)]

Create list with all combination of dictionaries, where key only appears once

I tried different approaces with itertools, but just can't figure it out.
I need to find different combinations of dictionaries:
letters = ['a','b','c']
combinations = []
for i in range(3):
for t in letters:
one_combi = {str(t):i}
combinations.append(one_combi)
Now have a list of dictionaries {letter:number}
Now I need to create a list of combinations where the key (letter) only appear once.
Expected output looks something like this:
[{'a':0,'b':0,'c':0},
{'a':1,'b':0,'c':0},
{'a':1,'b':1,'c':0},
{'a':1,'b':1,'c':1},
{'a':2,'b':0,'c':0},
...
{'a':2,'b':2,'c':2}]
Would be great if someone can help me out on this one!
You can generate all combinations of integers from a range derived from the length of the input, and then use zip:
letters = ['a','b','c']
def combos(d, c = []):
if len(c) == len(d):
yield dict(zip(letters, c))
else:
for i in d:
yield from combos(d, c+[i])
print(list(combos(range(len(letters))))
Output:
[{'a': 0, 'b': 0, 'c': 0},
{'a': 0, 'b': 0, 'c': 1},
{'a': 0, 'b': 0, 'c': 2},
{'a': 0, 'b': 1, 'c': 0},
{'a': 0, 'b': 1, 'c': 1},
...
{'a': 2, 'b': 2, 'c': 2}]
What you are looking for is itertools.product
from itertools import product
lst = []
for a, b, c in product([0, 1, 2], repeat=3):
lst.append({'a': a, 'b': b, 'c': c})
print(lst)
Output:
[{'a': 0, 'b': 0, 'c': 0},
{'a': 0, 'b': 0, 'c': 1},
{'a': 0, 'b': 0, 'c': 2},
{'a': 0, 'b':1, 'c': 0},
{'a': 0, 'b': 1, 'c': 1},
{'a': 0, 'b': 1, 'c': 2},
{'a': 0, 'b': 2, 'c': 0},
{'a': 0, 'b': 2, 'c': 1},...
Update
We can compact everything into a single line using list comprehension.
letters = ['a','b','c']
lst = [dict(zip(letters, x)) for x in product(range(len(letters)), repeat=len(letters))]
print(lst)

Extract keys from a list of dicts with single key-value pairs

I have a list which is like this:
[{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
How can I extract a list of keys from it?
[0, 0, 1, 0, 1, 0]
Use dict.keys() to get keys out of a dictionary and use it in a list-comprehension like below:
lst = [{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
print([y for x in lst for y in x.keys()])
# [0, 0, 1, 0, 1, 0]
Or, this should be further simplified as:
print([y for x in lst for y in x])
Because, when you simply iterate through dictionary like for y in x, you are actually iterating through keys of the dictionary.
Use dict.keys to extract the keys of each of the dict, convert to a list, and then extract the first element
>>> lst = [{0: 26}, {0: 36}, {1: 1}, {0: 215}, {1: 63}, {0: 215}]
>>> [list(d.keys())[0] for d in lst]
[0, 0, 1, 0, 1, 0]
Alternatively, you can use list comprehension as below
>>> [k for d in lst for k in d.keys()]
[0, 0, 1, 0, 1, 0]
I suggest you to simply iterate over the list of dictionaries, and to iterate over the keys of each dictionary, using list comprehension as follows:
myList = [{0: 26}, {0: 36}, {1: 1}, {0: 215}, {1: 63}, {0: 215}]
newList = [k for d in myList for k in d]
print(newList) # [0, 0, 1, 0, 1, 0]
Those are the keys so you would use the dict.keys() method:
L = [{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
L2 = [list(d.keys())[0] for d in L]
use keys() function of dict().
a = [{0: 26}, {0: 36}, {1: 1}, {0: 215}, {1: 63}, {0: 215}]
keys = list(a.keys()[0])
vaues = (a.values()[0])
Cheers!
you can do it this way :
list = [list(d.keys())[0] for d in originalList] # originalList is the first list you posted
Here's the output : [0, 0, 1, 0, 1, 0]
You can use dict.keys and itertools.chain to build a list of all keys:
from itertools import chain
w = [{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
keys = list(chain.from_iterable(map(dict.keys, w)))
OR try the below code, using map:
lod=[{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
print(list(map(lambda x: list(x.keys())[0])))

Python dict of dict setting values missmatch

Hi can anyone explain why we have the following behavior:
>>> k = [0.5, 1, 2]
>>> m = [0.5, 1, 2]
>>> dict1 = dict.fromkeys(k, dict.fromkeys(m, 0))
>>> dict1
{0.5: {0.5: 0, 1: 0, 2: 0}, 1: {0.5: 0, 1: 0, 2: 0}, 2: {0.5: 0, 1: 0, 2: 0}}
>>> dict1[0.5][0.5]= 4.5
>>> dict1
{0.5: {0.5: 4.5, 1: 0, 2: 0}, 1: {0.5: 4.5, 1: 0, 2: 0}, 2: {0.5: 4.5, 1: 0, 2: 0}}
>>> dict2 = {0.5: {0.5: 0, 1: 0, 2: 0}, 1: {0.5: 0, 1: 0, 2: 0}, 2: {0.5: 0, 1: 0, 2: 0}}
>>> dict2[0.5][0.5] = 4.5
>>> dict2
{0.5: {0.5: 4.5, 1: 0, 2: 0}, 1: {0.5: 0, 1: 0, 2: 0}, 2: {0.5: 0, 1: 0, 2: 0}}
so in the first case whenever I try to change a value of dict1 all the values with the same second key are changing (e.g dict1[0.5][0.5]=4.5 will also change dict1[1][0.5] for some reason).
I haven't found a good dupe target, although this general issue is common:
Because dict.fromkeys(m, 0) runs once when the function is called, and then puts the same dictionary in every value for dict.fromkeys(k, dict.fromkeys(m, 0)). You can check this by doing for dct in dict1: print id(dict1[dct]) they will all be the same id, eg. the same object:
>>> for dct in dict1: print id(dict1[dct])
140401847695352
140401847695352
140401847695352
This happens because parameters are only evaluated once, when the function is called... so dict.fromkeys will put the same value (whatever that may be) in every key. It is equivalent to:
default_dct = dict.fromkeys(m, 0)
dict1 = dict.fromkeys(k, default_dct)
Instead, you can use a dictionary comprehension:
dict1 = {key: {ikey: 0 for ikey in m} for key in k}
or, via copying:
inner_dct = dict.fromkeys(m, 0)
dict1 = {}
for key in k:
dict1[key] = inner_dct.copy()
Both of which yield different values:
>>>for dct in dict1: print id(dict1[dct])
140401847695352
140401847646200
140401847644240
Only one sub-dictionary was ever created. What you have now is that dictionary itself accessible in multiple ways.
dict[0.5] and dict[1] refer to the same dictionary (and not copies of it).
One way to achieve what you want to do is to use a dict comprehension:
dict1 = {k_outer: {k_inner:0 for k_inner in m} for k_outer in k}
This creates a new nested dict for each key, thereby avoiding the problem of them all accessing the same nested dict.

list to custom dictionary one liner

I have a list:
a = [0,1,2,5]
I want to get output something similar to:
output = [{'i':0,'v':0},{'i':1,'v':0},{'i':2,'v':0},{'i':5,'v':0}]
with one line of code/expression where:
output[indx]['i'] = a[indx]
and:
output[indx]['v'] = 0
Use a list comprehension to produce your output list:
[{'i': i, 'v': 0} for i in a]
This produces a list with the same number of elements as a has, each a dictionary wit hthe 'i' value set to an element from a:
>>> a = [0, 1, 2, 5]
>>> [{'i': i, 'v': 0} for i in a]
[{'v': 0, 'i': 0}, {'v': 0, 'i': 1}, {'v': 0, 'i': 2}, {'v': 0, 'i': 5}]

Categories