Reputation: 417
I have a list of dicts a and a list b as shown below. len(b) will not always be 4. it may be 2 or 3. I compared values in b to keys in each dict in b. I obtained the result shown, which should rightly be of length b but the order is not as desired.
My 2 problems are:
Here is what I have
a = [{0: 0, 1: 1, 2: 7, 3: 0}, {0: 0, 1: 0, 2: 12, 3: 0}, {0: 5, 1: 0, 2: 8, 3: 0}]
b = [2, 0, 1, 3]
c = []
for i in a:
x = []
for k, v in i.items():
if k == b[0]:
x.append(v)
if k == b[1]:
x.append(v)
if k == b[2]:
x.append(v)
if k == b[3]:
x.append(v)
c.append(x)
print(c)
My result is
[[0, 1, 7, 0], [0, 0, 12, 0], [5, 0, 8, 0]]
If you notice, for b = 2
i.e. b[0]
, the result from a[1
] is rightly 7 but it comes after 0 and 1 in my final result even though 2 is the first item in b.
Thank you
Upvotes: 1
Views: 71
Reputation: 53029
You could also use operator.itemgetter
which provides a neat shorthand to retrieve multiple items in one go:
>>> a = [{0: 0, 1: 1, 2: 7, 3: 0}, {0: 0, 1: 0, 2: 12, 3: 0}, {0: 5, 1: 0, 2: 8, 3: 0}]
>>> b = [2, 0, 1, 3]
>>>
>>> from operator import itemgetter
>>> get_b = itemgetter(*b)
Now for example:
>>> get_b(a[0])
(7, 0, 1, 0)
The result is a tuple, not a list. If that's ok you can make a list comprehension or use map:
>>> [get_b(d) for d in a]
[(7, 0, 1, 0), (12, 0, 0, 0), (8, 5, 0, 0)]
>>> list(map(get_b, a))
[(7, 0, 1, 0), (12, 0, 0, 0), (8, 5, 0, 0)]
If it must be lists, it's a bit more cumbersome:
>>> [list(get_b(d)) for d in a]
[[7, 0, 1, 0], [12, 0, 0, 0], [8, 5, 0, 0]]
>>> list(map(list, map(get_b, a)))
[[7, 0, 1, 0], [12, 0, 0, 0], [8, 5, 0, 0]]
On my system itemgetter
appears to be a bit faster than one-by-one lookup even if we have to convert tuples to lists:
>>> from timeit import timeit
>>>
>>> def multi_get(a, b):
... get_b = itemgetter(*b)
... return [[*get_b(d),] for d in a]
...
>>> def multi_get_tuple(a, b):
... get_b = itemgetter(*b)
... return [get_b(d) for d in a]
...
>>> timeit("multi_get_tuple(a, b)", globals=globals(), number=1000000)
0.9130640690000291
>>> timeit("multi_get(a, b)", globals=globals(), number=1000000)
1.0864301430010528
>>> timeit("[[d[i] for i in b] for d in a]", globals=globals(), number=1000000)
1.40757593699891
Upvotes: 1
Reputation: 1695
The problem is, you do not iterate with regards to b. You iterate with regards to key order in dictionaries inside a. Thats why output elements are out of order.
For the correct solution, you should iterate with regards to b's items, like this:
a = [{0: 0, 1: 1, 2: 7, 3: 0}, {0: 0, 1: 0, 2: 12, 3: 0}, {0: 5, 1: 0, 2: 8, 3: 0}]
b = [2, 0, 1, 3]
c = []
for a_dict in a:
x = []
for b_element in b:
if b_element in a_dict:
x.append(a_dict[b_element])
print("Appending %ss value %s to X! new X: %s" % (b_element, a_dict[b_element], str(x)))
c.append(x)
print(c)
Prints:
Appending Key:2s Value:7 to X! new X: [7]
Appending Key:0s Value:0 to X! new X: [7, 0]
Appending Key:1s Value:1 to X! new X: [7, 0, 1]
Appending Key:3s Value:0 to X! new X: [7, 0, 1, 0]
Appending Key:2s Value:12 to X! new X: [12]
Appending Key:0s Value:0 to X! new X: [12, 0]
Appending Key:1s Value:0 to X! new X: [12, 0, 0]
Appending Key:3s Value:0 to X! new X: [12, 0, 0, 0]
Appending Key:2s Value:8 to X! new X: [8]
Appending Key:0s Value:5 to X! new X: [8, 5]
Appending Key:1s Value:0 to X! new X: [8, 5, 0]
Appending Key:3s Value:0 to X! new X: [8, 5, 0, 0]
[[7, 0, 1, 0], [12, 0, 0, 0], [8, 5, 0, 0]]
Upvotes: 1
Reputation: 164623
You can use a list comprehension:
res = [[d[i] for i in b] for d in a]
print(res)
[[7, 0, 1, 0], [12, 0, 0, 0], [8, 5, 0, 0]]
To understand how this works, it sometimes helps to write out the full nested for
loop:
res = []
for d in a:
res_int = []
for i in b:
res_int.append(d[i])
res.append(res_int)
A functional approach is also possible:
res = [list(map(d.get, b)) for d in a]
Upvotes: 1