Reputation: 330
I have a dictionary consists of string or dictionaries
parameters = {
"date": {'date':"2015-12-13"},
"name": "Nick",
"product": "BT Adapter",
"transaction-id": ""
}
And i need to get list like ['2015-12-13', 'BT Adapter', 'Nick']
.
If it has no dictionaries in it, print filter(lambda x: x if len(x) > 0 and type(x) is not dict else None, parameters.values())
works perfectly, but with dictionary in it i tried to extract it's value with
print filter(lambda x: x if len(x) > 0 and type(x) is not dict else map(lambda y: x[y], x.keys()), parameters.values())
and i get AttributeError: 'str' object has no attribute 'keys'
.
How all values can be extracted?
Upvotes: 2
Views: 3161
Reputation: 46
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def magic(params):
dicts = [params]
values = []
while len(dicts):
d = dicts.pop()
for value in d.values():
if isinstance(value, dict):
dicts.append(value)
elif isinstance(value, basestring) and len(value) > 0:
values.append(value)
return values
def main():
parameters = {
"date": {'date': "2015-12-13"},
"name": "Nick",
"product": "BT Adapter",
"transaction-id": ""
}
print(magic(parameters))
if __name__ == '__main__':
main()
Upvotes: 1
Reputation: 330
It is failing for the last attribute "transaction-id" because its length is 0.
This would work for you:
print filter(lambda x: x if len(x) >= 0 and type(x) is not dict else map(lambda y: x[y], x.keys()), parameters.values())
Upvotes: 0
Reputation: 8254
It looks to me like you are trying to do a map
operation with your filter
, showing a bit of a misunderstanding. The function in a filter essentially must return True
or False
to determine whether to keep a certain value in the second argument, so it does not make sense to filter in this way:
filter(lambda x: x if len(x) > 0 and type(x) is not dict else None, parameters.values())
Better:
filter(lambda x: len(x) > 0 and type(x) is not dict, parameters.values())
# better idiom
filter(lambda x: len(x) > 0 and not isinstance(x, dict), parameters.values())
This is especially true for your next attempt, which attempts to map
inside a filter function. It's better to think of this as a two-step process:
lex(x) < 1
)Let's try this instead, if you need to use filter
. I'm using Python3, which means that I have to do a lot of ugly list
casting:
list(map(lambda x: list(x.values()) if isinstance(x, dict) else x, filter(lambda x: len(x) > 0, parameters.values())))
This produces:
[['2015-12-13'], 'BT Adapter', 'Nick']
This is not pythonic at all: filter
and map
are much more at home in list comprehensions like the following:
[list(v.values()) if isinstance(v, dict) else v for v in parameters.values() if len(v) > 0]
Let me know if you also need to flatten the list after getting the dictionary values.
Upvotes: 2
Reputation: 101959
You are misusing the filter
function. The argument to filter
is a predicate, which is a function that returns a true/false value and filter returns the elements from the second argument for which that function returns true.
For example:
print(list(filter(lambda x: 5, [1,2,3,4,5])))
[1, 2, 3, 4, 5]
Since bool(5) == True
filter returns all the elements.
Since you pass as second argument the value parameters.values()
there is no way to obtain your expected result by just passing a predicate to filter.
What you want to do is something like:
from itertools import chain
def listify(value):
if isinstance(value, dict):
return value.values()
elif value:
return [value]
return ()
print(list(chain.from_iterable(map(listify, parameters.values()))))
So first you convert the values into sequences, and then you concatenate them using chain.from_iterable
.
The empty values are removed by listify
since it returns an empty sequence in that case. Sample run:
In [2]: parameters = {
...: "date": {'date':"2015-12-13"},
...: "name": "Nick",
...: "product": "BT Adapter",
...: "transaction-id": ""
...: }
In [3]: print(list(chain.from_iterable(map(listify, parameters.values()))))
['2015-12-13', 'Nick', 'BT Adapter']
Also: it doesn't make sense to write a complex lambda function with nested conditionals, so just use a def
and write it properly. lambda
s are fine only if they are extremely short.
Upvotes: 4