4

I defined a dictionary like this (list is a list of integers):

my_dictionary = {'list_name' : list, 'another_list_name': another_list}

Now, I want to create a new list by iterating over this dictionary. In the end, I want it to look like this:

my_list = [list_name_list_item1, list_name_list_item2, 
                list_name_list_item3, another_list_name_another_list_item1]

And so on.

So my question is: How can I realize this?

I tried

for key in my_dictionary.keys():
    k = my_dictionary[key]
    for value in my_dictionary.values():
        v = my_dictionary[value]
        v = str(v)
        my_list.append(k + '_' + v)

But instead of the desired output I receive a Type Error (unhashable type: 'list') in line 4 of this example.

Bhargav Rao
  • 41,091
  • 27
  • 112
  • 129
Hisushi
  • 67
  • 1
  • 9

6 Answers6

5

You're trying to get a dictionary item by it's value whereas you already have your value.

Do it in one line using a list comprehension:

my_dictionary = {'list_name' : [1,4,5], 'another_list_name': [6,7,8]}

my_list = [k+"_"+str(v) for k,lv in my_dictionary.items() for v in lv]

print(my_list)

result:

['another_list_name_6', 'another_list_name_7', 'another_list_name_8', 'list_name_1', 'list_name_4', 'list_name_5']

Note that since the order in your dictionary is not guaranteed, the order of the list isn't either. You could fix the order by sorting the items according to keys:

my_list = [k+"_"+str(v) for k,lv in sorted(my_dictionary.items()) for v in lv]
Jean-François Fabre
  • 126,787
  • 22
  • 103
  • 165
  • thank you so much for your help! I'm pretty new to Python so I appreciate your explanation and solution! :) – Hisushi Nov 11 '16 at 10:35
2

Try this:

my_list = []
for key in my_dictionary:
    for item in my_dictionary[key]:
        my_list.append(str(key) + '_' + str(item))

Hope this helps.

Jean-François Fabre
  • 126,787
  • 22
  • 103
  • 165
Tarun Gupta
  • 494
  • 7
  • 11
0

You've written an outer loop over keys, then an inner loop over values, and tried to use each value as a key, which is where the program failed. Simply use the dictionary's items method to iterate over key,value pairs instead:

["{}_{}".format(k,v) for k,v in d.items()]

Oops, failed to parse the format desired; we were to produce each item in the inner list. Not to worry...

d={1:[1,2,3],2:[4,5,6]}
list(itertools.chain(*(
      ["{}_{}".format(k,i) for i in l]
      for (k,l) in d.items() )))

This is a little more complex. We again take key,value pairs from the dictionary, then make an inner loop over the list that was the value and format those into strings. This produces inner sequences, so we flatten it using chain and *, and finally save the result as one list.

Edit: Turns out Python 3.4.3 gets quite confused when doing this nested as generator expressions; I had to turn the inner one into a list, or it would replace some combination of k and l before doing the formatting.

Edit again: As someone posted in a since deleted answer (which confuses me), I'm overcomplicating things. You can do the flattened nesting in a chained comprehension:

["{}_{}".format(k,v) for k,l in d.items() for v in l]

That method was also posted by Jean-François Fabre.

Yann Vernier
  • 13,812
  • 2
  • 24
  • 25
0

Your immediate problem is that dict().values() is a generator yielding the values from the dictionary, not the keys, so when you attempt to do a lookup on line 4, it fails (in this case) as the values in the dictionary can't be used as keys. In another case, say {1:2, 3:4}, it would fail with a KeyError, and {1:2, 2:1} would not raise an error, but likely give confusing behaviour.

As for your actual question, lists do not attribute any names to data, like dictionaries do; they simply store the index.

def f()
    a = 1
    b = 2
    c = 3
    l = [a, b, c]

    return l

Calling f() will return [1, 2, 3], with any concept of a, b, and c being lost entirely.

If you want to simply concatenate the lists in your dictionary, making a copy of the first, then calling .extend() on it will suffice:

my_list = my_dictionary['list_name'][:]
my_list.extend(my_dictionary['another_list_name'])

If you're looking to keep the order of the lists' items, while still referring to them by name, look into the OrderedDict class in collections.

Adam Barnes
  • 2,201
  • 15
  • 22
  • It's not that values() is a generator; it's that values aren't keys. In his case, they're lists, which aren't hashable and thus *can't* be keys, so he gets a type error instead of a key error. – Yann Vernier Nov 11 '16 at 10:08
  • I worded it poorly; it should say "is" rather than "returns". – Adam Barnes Nov 11 '16 at 10:09
0

Use list comprehensions like this

d = {"test1":[1,2,3,],"test2":[4,5,6],"test3":[7,8,9]}

new_list = [str(item[0])+'_'+str(v) for item in d.items() for v in item[1]]

Output:

new_list:
['test1_1',
 'test1_2',
 'test1_3',
 'test3_7',
 'test3_8',
 'test3_9',
 'test2_4',
 'test2_5',
 'test2_6']
feng.zhai
  • 31
  • 4
0

Let's initialize our data

In [1]: l0 = [1, 2, 3, 4]

In [2]: l1 = [10, 20, 30, 40]

In [3]: d = {'name0': l0, 'name1': l1}

Note that in my example, different from yours, the lists' content is not strings... aren't lists heterogeneous containers?

That said, you cannot simply join the keys and the list's items, you'd better cast these value to strings using the str(...) builtin.

Now it comes the solution to your problem... I use a list comprehension with two loops, the outer loop comes first and it is on the items (i.e., key-value couples) in the dictionary, the inner loop comes second and it is on the items in the corresponding list.

In [4]: res = ['_'.join((str(k), str(i))) for k, l in d.items() for i in l]

In [5]: print(res)
['name0_1', 'name0_2', 'name0_3', 'name0_4', 'name1_10', 'name1_20', 'name1_30', 'name1_40']

In [6]: 

In your case, using str(k)+'_'+str(i) would be fine as well, but the current idiom for joining strings with a fixed 'text' is the 'text'.join(...) method. Note that .join takes a SINGLE argument, an iterable, and hence in the list comprehension I used join((..., ...)) to collect the joinands in a single argument.

gboffi
  • 17,041
  • 5
  • 45
  • 76
  • post scriptum — As others have said in their answers, you have NO GUARANTEE about any particular order of the `k, l` items retrieved in the outer loop. – gboffi Nov 11 '16 at 10:43