python3 value returned wrong with container variable - python-3.x

I meet a code that failed to meet my expectation. Details as below:
a = ['name']
b = [('name=cheng',),('name=huang',),('name=pan',)]
Dict = {}
c = []
for i in range(0,3):
for j in range(0,1):
Dict[a[j]] = b[i][j]
c.append(Dict)
print(c)
>>> [{'name':'name=pan'},{'name':'name=pan'},{'name':'name=pan'}]
what i expected should be
>>> [{'name':'name=cheng'},{'name':'name=huang'},{'name':'name=pan'}]
So could you please tell me how to solve the issue ?

You are changing the value of Dict in place and not creating a new dictionary every time. Each iteration of the loop, you are setting Dict["name"] equal to one of the elements in b and then appending it to the list. The next iteration of your loop changes dict in place (meaning the previous version you appending to c will also be changed). The result is that your list c is filled with 3 exact copies (exact same location in memory) of the dictionary created in the final iteration of the loop.
How do you fix this? Make a new dictionary every time.
a = ['name']
b = [('name=cheng',),('name=huang',),('name=pan',)]
c = []
for i in range(0,3):
for j in range(0,1):
temp_dict = {a[j]: b[i][j]}
c.append(temp_dict)
print(c)
Result:
[{'name': 'name=cheng'}, {'name': 'name=huang'}, {'name': 'name=pan'}]

You use the same value of Dict for all of the iterations of the loop. So all of the dictionaries are the same. You just have three copies of the same dictionary in the list.
If you move the Dict = {} statement into the loop, it will be fixed.
a = ['name']
b = [('name=cheng',),('name=huang',),('name=pan',)]
c = []
for i in range(0,3):
Dict = {}
for j in range(0,1):
Dict[a[j]] = b[i][j]
c.append(Dict)
print(c)
Or more Pythonic:
keys = ['name']
values_list = [('name=cheng',), ('name=huang',), ('name=pan',)]
result = []
for values in values_list:
result.append(dict(zip(keys, values)))
print(result)
This works by using the zip builtin which does the same thing as [(x[i], y[i]) for i in range(min(len(x), len(y))] without needing to keep track of the indices or lengths.
The dict class can build a dictionary from a list of tuples, which is what this solution uses.

Related

How to update multiple key and values in dictionary?

Input:
lst = {"scot":{"23", "poland"}, "martin":{"32", "england"},
"":{"23", "ireland"},None:{"23", "indian"}, "1":{"", ""}}
Output:
lst2 = {"scot":{"230", "poland"}, "martin":{"320", "england"},
"":{"230", "ireland"},None:{"230", "indian"}, "10":{"", ""}}
Code :
for k,v in lst.items():
if k is not None:
if k.isdigit():
k = k + "0"
print(k)
if v is not None and type(v) == set :
for i in v:
if i.isdigit():
i =i + "0"
print (i)
print(lst)
But The values are not getting updated and I am getting below output:
{'scot': {'23', 'poland'}, 'martin': {'england', '32'}, '': {'23', 'ireland'}, None: {'23', 'indian'}, '1': {''}}
You were on the right track, but you were not updating the actual values in your dictionary. More importantly, you shouldn't be changing the iterables (dicts, sets, lists) while iterating through them - in the sense that you shouldn't be adding or removing new elements that would normally be part of the iteration (like add or remove key-value pairs while iterating through a dictionary).
Because of that, a simple implementation of your program needs to have two parts - first where you search your dictionary lst and record the necessary changes, and second where you implement those recorded changes.
Below code does just that,
lst = {"scot":{"23", "poland"}, "martin":{"32", "england"}, "":{"23", "ireland"},None:{"23", "indian"}, "1":{"", ""}}
keys_to_delete = []
kv_pairs_to_add = {}
ks_pairs_to_swap = []
# Collect what needs to change
for k,v in lst.items():
if k is not None:
if k.isdigit():
# Save the old key for later deletion
keys_to_delete.append(k)
# Create and record the new entry
kv_pairs_to_add[k + "0"] = v
k += "0"
print(k)
# ------ Was this supposed to be v or k?
if v is not None and type(v) == set:
for i in v:
if i.isdigit():
# Record the key, old value,
# and new value as a sublist
ks_pairs_to_swap.append([k, i, i+"0"])
i += "0"
print (i)
# Implement the changes
# Dictionary keys
for key in keys_to_delete:
del lst[key]
lst.update(kv_pairs_to_add)
# Sets
for key, ov, nv in ks_pairs_to_swap:
lst[key].remove(ov)
lst[key].add(nv)
print(lst)
Note the comment with ------ - just checking if vthere is not a bug, it seems your keys can be None, so the check should maybe be for k and not v.
I never put the code into my IDE but looking at your code it does seem to be right but I noticed that you're not ACTUALLY updating your list, your only creating variable.
For example
for k,v in lst.items():
This creates two variables k,v and you use those variables in the for loop with if statements, in order to actually update it you'd have to figure out a way to remove the previous string and append the new updated variable k or v
In the if statement you're not actually appending and removing anything from the dictionary you are only adding something to k or v and printing the variable out.
I don't think I wrote this well, but I hope I gave you a possible clear understanding on why its not actually getting updated.
lst = {"scot": {"23", "poland"}, "martin": {"32", "england"}, "": {"23", "ireland"}, None: {"23", "indian"}, "1": {"", ""}}
result = {}
for k, v in lst.items():
if k is not None or str:
for i in v:
if i.isdigit():
i += "0"
else:
j = i
result[k] = {i, j}
print(result)
Output:
{'scot': {'poland', '230'}, 'martin': {'england', "320"}, '': {'230', 'ireland'}, None: {'230', 'indian'}, '1': {''}}

Pop element at the beginning or at the last from dict in python3

Collection module in python has a datatype OrderedDict(), which enables us to save the key value pairs in the same order as they were inserted in dict. It has a method popitem, which allows us to pop items at the beginning or at the last
dict.popitem(last = True)
dict.popitem(last = False)
The default dict in python 3 also works as OrderedDict(), i.e., it maintains the order of key value pairs as they were inserted. But how to remove the first or the last element from this dict as in OrderedDict(), without using the traditional for loop for accessing the key value pair as
for key, value in dict:
dict.pop(key)
break
I just want to know if there is any inbuild function for popping the first or the last element in default dict of python 3 as in OrderedDict(). I searched but couldn't find any.
Since Python 3.6, dict.popitem always acts as a LIFO like OrderedDict.popitem(last=True) does.
But while there is no direct support from dict to make the popitem method pop from the front of the dict, you can simulate the FIFO behavior of OrderedDict.popitem(last=False) such as:
d = OrderedDict(a=1, b=2)
k, v = d.popitem(last=False) # k = 'a', v = 1
by getting the first key of the dict and then popping that key for its value:
d = {'a': 1, 'b': 2}
k = next(iter(d)) # k = 'a'
v = d.pop(k) # v = 1

Convert elements in ONE list to keys and values in a dictionary

I'm looking for a way to convert a list to a dictionary as shown below. Is this possible? Thanks in advance.
list = ["1/a", "2/b", "3/c"]
dict = {"1": "a", "2": "b", "3": "c"}
Of course it is possible.
First, you can split an element of the list with e.split("/"), which will give a list for example splitted = ["1", "a"].
You can assign the first element to the key and the second to the value:
k = splitted[0]
v = splitted[1]
or another way to express that:
k,v = splitted
Then you can iterate over your list to build your dict, so if we wrap this up (you should not call a list list because list is a type and an already existing identifier:
d = {}
for e in elements:
k,v = e.split("/")
d[k] = v
You can also do that in one line with a dict comprehension:
d = {k:v for k,v in [e.split("/") for e in elements]}
Yes you can.
If you want to have everything after the '/' (i.e. 2nd char), you can do:
dict = {c[0]:c[2:] for c in list}
If you want to have everything after the '/' (but may not be the 2nd char), you can do:
dict = {c[0]:c.split('/')[1] for c in list}
It really dependes on the input you have and what output you want
You can do like this.
lista = ["1/a", "2/b", "3/c"]
new_dict = {}
for val in lista:
new_dict.update({val[0]:val[2]})
print(new_dict)
Try this
list = ["1as/aasc", "2sa/bef", "3edc/cadeef"]
dict = {i.split('/')[0]:i.split('/')[1] for i in list}
Answer will be
{'1as': 'aasc', '2sa': 'bef', '3edc': 'cadeef'}
I have given a different test case. Hope this will answer your question.

Nesting dictionaries with a for loop

I am trying to add a dictionary within a dictionary in the current code like this!
i = 0
A ={}
x = [...]
for i in x:
(a,b) = func(x)#this returns two different Dictionaries as a and b
for key in a.keys():
A[key] = {}
A[key][i] = a[key]
print('A:',A)
as I executed it, I am getting 'A' dictionary being printed throughout the loop! But, i need them in one single dictionary say: "C"
How do I do that?

How to get keys from nested dictionary of arbitrary length in Python

I have a dictionary object in python. Let's call it as dict. This object could contain another dictionary which may in turn contain another dictionary and so on.
dict = { 'k': v, 'k1': v1, 'dict2':{'k3': v3, 'k4':v4} , 'dict3':{'k5':v5, dict4:{'k6':v6}}}
This is just an example. Length of outermost dictionary could be anything. I want to extract keys from such dictionary object in following two ways :
get list of only keys.
[k,k1,k2,k3,k4,k5,k6]
get list of keys and its parent associated dictionary so something like this :
outer_dict_keys = [k ,dict2, dict3]
dict2_keys = [k3,k4]
dict3_keys = [k5, dict4]
dict4_keys = [k6]
Outermost dictionary dict length is always changing so I can not hard code anything.
What is best way to achieve above result ?
Use a mix of iteration and tail recursion. After quoting undefined names, making spacing uniform, and removing 'k2' from the first result, I came up with the code below. (Written and tested for 3.4, it should run on any 3.x and might on 2.7.) A key thing to remember is that the iteration order of dicts is essentially random, and varies with each run. Recursion as done here visit sub-dicts in depth-first rather than breadth-first order. For dict0, both are the same, But if dict4 were nested in dict2 rather than dict3, they would not be.
dict0 = {'k0': 0, 'k1': 1, 'dict2':{'k3': 3, 'k4': 4},
'dict3':{'k5': 5, 'dict4':{'k6': 6}}}
def keys(dic, klist=[]):
subdics = []
for key in sorted(dic):
val = dic[key]
if isinstance(val, dict):
subdics.append(val)
else:
klist.append(key)
for subdict in subdics:
keys(subdict, klist)
return klist
result = keys(dict0)
print(result, '\n', result == ['k0','k1','k3','k4','k5','k6'])
def keylines(dic, name='outer_dict', lines=[]):
vals = []
subdics = []
for key in sorted(dic):
val = dic[key]
if isinstance(val, dict):
subdics.append((key,val))
else:
vals.append(key)
vals.extend(pair[0] for pair in subdics)
lines.append('{}_keys = {}'.format(name, vals))
for subdict in subdics:
keylines(subdict[1], subdict[0], lines)
return lines
result = keylines(dict0)
for line in result:
print(line,)
print()
expect = [
"outer_dict_keys = ['k0', 'k1', 'dict2', 'dict3']",
"dict2_keys = ['k3', 'k4']",
"dict3_keys = ['k5', 'dict4']",
"dict4_keys = ['k6']"]
for actual, want in zip(result, expect):
if actual != want:
print(want)
for i, (c1, c2) in enumerate(zip(actual, want)):
if c1 != c2:
print(i, c1, c2)

Resources