6

I have this list of dictionary:

MylistOfdict = [
    {'Word': 'surveillance',
     'Word No': 1},
    {'Word': 'equivocal',
     'Word No': 2}]

I want to create a new list of dictionary (word_db2) that has 3 dictionaries for each dictionary in MylistOfdict. In addition to key and values of MylistOfdict, each of those dictionary should have 'Card Type' key with value Type 1, Type 2, Type 3 and 'Card Key' key with incremental value

Code:

word_db2 = []

key = 1
for i in MylistOfdict:
    for j in range(1, 4):
        i['Card Type'] = 'Type ' + str(j)
        i['Card Key'] = key
        print(i)
        
        word_db2.append(i)
        key += 1

Output:

{'Word': 'surveillance', 'Word No': 1, 'Card Type': 'Type 1', 'Card Key': 1}
{'Word': 'surveillance', 'Word No': 1, 'Card Type': 'Type 2', 'Card Key': 2}
{'Word': 'surveillance', 'Word No': 1, 'Card Type': 'Type 3', 'Card Key': 3}
{'Word': 'equivocal', 'Word No': 2, 'Card Type': 'Type 1', 'Card Key': 4}
{'Word': 'equivocal', 'Word No': 2, 'Card Type': 'Type 2', 'Card Key': 5}
{'Word': 'equivocal', 'Word No': 2, 'Card Type': 'Type 3', 'Card Key': 6}

This output is correct, but word_db2 stores only last appended value in every iteration:

[{'Word': 'surveillance', 'Word No': 1, 'Card Type': 'Type 3', 'Card Key': 3},
 {'Word': 'surveillance', 'Word No': 1, 'Card Type': 'Type 3', 'Card Key': 3},
 {'Word': 'surveillance', 'Word No': 1, 'Card Type': 'Type 3', 'Card Key': 3},
 {'Word': 'equivocal', 'Word No': 2, 'Card Type': 'Type 3', 'Card Key': 6},
 {'Word': 'equivocal', 'Word No': 2, 'Card Type': 'Type 3', 'Card Key': 6},
 {'Word': 'equivocal', 'Word No': 2, 'Card Type': 'Type 3', 'Card Key': 6}]
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Beginner
  • 1,202
  • 2
  • 20
  • 29
  • 4
    You reuse the same dictionary. Instead make copies of the dictionary. Inside `for j in range(1, 4):` put as the first line of the inner loop `i = dict(i)`. Also `i` is a bad name for a dictionary. Convention would be to call it `d`. `i` usually denotes an integer. – Steven Rumbalski Oct 19 '17 at 17:06
  • Related: [List of lists changes reflected across sublists unexpectedly](https://stackoverflow.com/q/240178/4518341) – wjandrea Nov 29 '20 at 19:58
  • Same problem as https://stackoverflow.com/questions/2612802/how-do-i-clone-a-list-so-that-it-doesnt-change-unexpectedly-after-assignment, but again for dicts rather than lists. – Karl Knechtel Aug 11 '22 at 08:47

4 Answers4

6

Let's review the loop body logic step by step:

  1. take one of the dicts
  2. modify it
  3. append it to the end of the list

So the key point you missed is that you modify and append the same object that was selected on the first step. And at the end of the snippet word_db2 contains six object refs, but only two unique. As a result, the output shows similar rows.

You can make a shallow copy of a dict before modifying and appending it:

for j in range(1, 4):
    i = dict(i)
    i['Card Type'] = 'Type '+str(j)
    i['Card Key'] = key
    print(i)

    word_db2.append(i)
    key += 1

As further note, if the dict contains other mutable objects like nested dicts, you should make a deep copy:

import copy
old_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
new_dict = copy.deepcopy(old_dict)
old_dict['a'][1] = 7
new_dict['a'][1] # 2
Oleh Rybalchenko
  • 6,998
  • 3
  • 22
  • 36
4

When you append a dictionary to a list, a reference to the original object itself is appended. So, you are currently just modifying the existing object's keys and values in each iteration of the inner loop, so the last written value is the only thing which persists.

To do what you require, you would need to create a new dictionary object in each iteration of the inner loop. For the shown dictionaries in MylistOfdict, a simple dictionary comprehension would work. But if you have more complex dictionaries, use the copy module's deepcopy method.

MylistOfdict = [{'Word': 'surveillance', 'Word No': 1}, 
                {'Word': 'equivocal', 'Word No': 2}]
word_db2 = []

key = 1
for i in MylistOfdict:
    for j in range(1, 4):
        # Creating a new dictionary object and copying keys and values from i
        new_dict = {k: v for k, v in i.items()}
        new_dict['Card Type'] = 'Type '+str(j)
        new_dict['Card Key'] = key

        print(new_dict)

        word_db2.append(new_dict)
        key += 1
suripoori
  • 311
  • 2
  • 10
1

Don't use the same dict, make copies of them:

word_db2 = []

key = 1
for i in MylistOfdict:
    for j in range(1, 4):
        i = dict(i)
        i['Card Type'] = 'Type '+str(j)
        i['Card Key'] = key
        print(i)

        word_db2.append(i)
        key += 1
developer_hatch
  • 15,898
  • 3
  • 42
  • 75
-1

Use deepcopy. What's happening is that your append call is just appending a reference to the original object.

from copy import deepcopy

my_list_of_dict = [{'Word': 'surveillance',
                    'Word No': 1},
                   {'Word': 'equivocal',
                    'Word No': 2}]
word_db2 = []

key = 1
for i in my_list_of_dict:
    for j in range(1, 4):
        i['Card Type'] = 'Type ' + str(j)
        i['Card Key'] = key
        print(i)
        word_db2.append(deepcopy(i))
        key += 1


for i in word_db2:
    print(i)
  • Good idea, but note that selected object (i) still modified before copying. So you should make a copy and then modify it. Please edit the snippet – Oleh Rybalchenko Oct 19 '17 at 17:24
  • `deepcopy` is unnecessary. A shallow copy is sufficient. The easiest way to make a shallow copy of a dictionary is to pass it to `dict` (as in `copy_of_d = dict(d)`). Also making the copy after modification mutates the contents of `MylistOfdict` which may be an unwanted side effect. To avoid this the copy should be made at the top of the inner loop. – Steven Rumbalski Oct 19 '17 at 17:24