I am trying to filter a JSON array based on a value contained in each of the arrays. Provided some user input, I only want to return relevant values based on the type.
Sample json:
{
"routes": [
{
"name": "Golden Shower",
"type": "Boulder",
"rating": "V5",
"stars": 5,
"starVotes": 131,
"pitches": ""
},
{
"name": "Girls Only",
"type": "Sport",
"rating": "5.10a",
"stars": 4.9,
"starVotes": 156,
"pitches": "1"
}
],
"success": 1
}
I have attempted to use a few variations of a code sample provided on a similar question, but am running in to issues due to slightly more complex data structure. See: how to filter json array in python
I have tried a few variations of code. All of the errors center around improper use of indices. Some things I've tried.
routeData = routesByGpsResp.json()
input_dict = routeData
output_dict = [x for x in input_dict if x['type'] == 'Sport']
error: sting indices must be integers
output_dict = [x for x in input_dict.items() if ['routes'][x]['type'] == 'Sport']
error: list indices must be integers or slices, not tuple
I was able to print a list of route names using for statement an index, but cannot seem to figure it out for list comprehension.
for key in range(len(routeData['routes'])):
print(routeData['routes'][key]['name'])
Is list comprehension the wrong way to go here? Should I be using a for statement instead?
Any help is appreciated!
Note: all your attempts are list comprehensions, while the variable name suggests a dict comprehension ([Python]: PEP 274 -- Dict Comprehensions).
Here's an example how you could get output_dict (and below, output_list) based on your input_dict, and the condition(s). As a note: the dict comprehension nests a list comprehension (which alone constitutes the 2nd example) for the "routes" key, while for all the other keys leaves the values unchanged:
>>> output_dict = {k: v if k != "routes" else [i for i in v if i["type"] == "Sport"] for k, v in input_dict.items()}
>>>
>>> from pprint import pprint
>>>
>>> pprint(output_dict)
{'routes': [{'name': 'Girls Only',
'pitches': '1',
'rating': '5.10a',
'starVotes': 156,
'stars': 4.9,
'type': 'Sport'}],
'success': 1}
>>>
>>> # Or if you only want the list
...
>>> output_list = [i for i in input_dict.get("routes", list()) if i["type"] == "Sport"]
>>>
>>> pprint(output_list)
[{'name': 'Girls Only',
'pitches': '1',
'rating': '5.10a',
'starVotes': 156,
'stars': 4.9,
'type': 'Sport'}]
In your first attempt:
output_dict = [x for x in input_dict if x['type'] == 'Sport']
x in input_dict iterates over the keys, so you are doing "routes"['type'], but strings are indexes by integers, and not by a string,
hence the error string indices must be integers
In your second attempt:
output_dict = [x for x in input_dict.items() if ['routes'][x]['type'] == 'Sport']
['routes'] is a list, and x is a tuple of (key,value) obtained by iterating over the dictionary input_dict, so ['routes'][x] raises a error: list indices must be integers or slices, not tuple
The right way for a list-comprehension, as already pointed out by #cs95, is to iterate over the list of routes by for x in input_dict.get('routes'), get the dictionary for the value of key type that matches Sport
print([x for x in input_dict.get('routes') if x.get('type') == 'Sport'])
The output is
[{'name': 'Girls Only', 'type': 'Sport', 'rating': '5.10a', 'stars': 4.9, 'starVotes': 156, 'pitches': '1'}]
Related
I have a Python list called "results" that has dictionaries as its values:
results = [
{
'postingStatus': 'Active',
'postEndDate': '1601683199000',
'boardId': '_internal',
'postStartDate': '1591084714000)'
},
{
'postingStatus': 'Expired',
'postEndDate': '1601683199000',
'boardId': '_external',
'postStartDate': '1591084719000)'
}
]
How would I create a list that gets all the values from the dictionary where the 'boardID' value is '_internal' (but ignores the dictionary where 'boardID' is '_external')? As an end result, I'm hoping for a list with the following contents:
['Active','1601683199000','_internal','1591084714000']
You can use list-comprehension:
out = [v for d in results for v in d.values() if d["boardId"] == "_internal"]
print(out)
Prints:
['Active', '1601683199000', '_internal', '1591084714000)']
without using List-comprehension
for items in results:
if items['boardId'] == '_internal':
lst1 = list(items.values())
print(lst1)
So I have a list like this:
[
{ '1234' : 'Sun'}
{ '353453' : 'Earth'}
...
]
I want to convert this to
[
{ 'temp': '1234', 'planet' : 'Sun'}
{ 'temp': '353453', 'planet': 'Earth'}
...
]
What's the Pythonic way to have this list of dict converted?
You can use a nested list/dictionary comprehension:
lst = [
{ '1234' : 'Sun'},
{ '353453' : 'Earth'}
]
out = [ { 'temp': k, 'planet': v } for l in lst for k, v in l.items() ]
# [{'temp': '1234', 'planet': 'Sun'}, {'temp': '353453', 'planet': 'Earth'}]
You shouldn’t try to do it in a one liner, it wouldn't be readable. You should construct the output list for each element one by one.
The fellow that will read your code later (and this might be yourself in a couple of month) will thank you.
You can do something like
a = [{'1234': 'Sun', "12": "mars"}]
output = []
for measurement in a:
output_list_of_dict = [{"temp": temp, "planet": planet} for temp, planet in measurement.items()]
output.extend(output_list_of_dict)
Note that in each loop turn, the result is a list itself : one dictionary may contain several temperatures. Therefore I used «extend» method, which merges the lists, and not append.
Doing it in an explicit and readable way conforms to the python philosophy.
It is the most pythonic in this sense.
Out of my head I would write something like below:
>>> l = [{ '1234' : 'Sun'}, { '353453' : 'Earth'}]
>>> r = [{'temp':list(x.keys())[0], 'planet': x[list(x.keys())[0]]} for x in l if len(x) == 1]
The core issue is that this will only work with a correctly formed input data structure, and this is not guaranteed in any way by the input format. Hencefore a prior validation of input data may be useful.
But if I had to put that kind of thing in production code, I would certainly avoid oneliner as suggested by #François-Dailloux and write instead the stupid and simple code below, which is also very readable and also likely faster:
l = [{ '1234' : 'Sun'}, { '353453' : 'Earth'}]
res = []
for d in l:
if len(d) == 1:
for k, v in d.items():
res.append('temp': k, 'planet': v)
If the len is not as expected I would also of course raise some exception to avoid propagation of broken input data.
I am trying to sort a list by keys of dict that has keys as a lists with other dicts.
big_list = [{"Key1": 1, "Key2" :2, "Key3": [{"sortable_key": value1, value2, ..}]}]
The goal is to sort the big_list by sortable_key. I am trying to accomplish sorting by using lambda:
big_list = list(sorted(big_list, key=lambda x: x["Key3"]["sortable_key"]))
But this is not working (not sorting the list, I assume that it would sort it in particular order like alphabetical). Am I trying to access the sortable_key in a wrong manner or is this not achievable by using lambda?
Thanks!
Full example of big_list:
big_list = [{'number': '7',
'code': '1106',
'name': 'John',
'det': [{'subcode': 'AA11','subname': 'Alan','age': 11},
{'subcode': 'CC11','subname': 'Hugo','age': 22},
{'subcode': 'BB11','subname': 'Walt','age': 18}]}]
In this case I need to sort list by 'subcode'.
You forgot a [0] since x["Key3"]is a list:
big_list = list(sorted(big_list, key=lambda x: x["Key3"][0]["sortable_key"]))
I'm working with a list that contains three different categories that I'm interested in: integers, floats, and strings. I want to convert that list into a dictionary and make each of these three categories into a key, then assign each element of that list to the appropriate key-value (e.g. if an element in the list is a string, then it will be assigned to the value for the "string" key). So for instance:
sample_list = [1.23, 34.34, 'abc', 'xyz', 22, 104]
the structure of the dictionary should be something like this:
new_dict = {"integers" : [list of all ints],
"floats" : [list of all floats],
"strings" : [list of all strings],
}
From the sample list above, the output would look like this:
list_to_dict = {'float': [1.23, 34.34],
'integer', [22, 104],
'string', ['abc', 'xyz']
}
I'm using dict() and zip() to convert the dictionary into a list, but I'm not sure how to build in the conditionals to place each element of the original list into the correct key-value pair. Here's what I have so far:
keys = ['integers', 'floats', 'strings']
values = [1.23, 34.34, 'abc', 'xyz', 22, 104]
mixed_dictionary = dict(zip(keys,values))
Is this the correct approach or should I be doing something differently? If it is the correct approach, how can I add conditionals to place all of the list elements into the appropriate key value? I tried working with a for loop and .append(), but neither worked.
You want to group your data based on some predicate. The essential steps are:
Identify what group the item belongs to
Place the item into the corresponding "bucket" for that group.
There are quite a few ways of achieving this. Some more direct than others.
Option 1
This is a good use case for itertools.groupby. The predicate here is the type of an element.
Note, however, that this won't work if your data isn't sorted by type to begin with. This could cause problems, so pre-sort your data first:
sample_list.sort(key=lambda x: id(type(x)))
Now, call groupby, and unpack each group as a list inside a dict comprehension:
from itertools import groupby
{i.__name__ : list(g) for i, g in groupby(sample_list, key=type)}
{
"float": [
1.23,
34.34
],
"str": [
"abc",
"xyz"
],
"int": [
22,
104
]
}
Option 2
Here's an alternative approach using groupby from the pandas API. This does not require a pre-sorting step.
import pandas as pd
s = pd.Series(sample_list)
s.groupby(s.map(lambda x: type(x).__name__)).apply(list).to_dict()
{
"float": [
1.23,
34.34
],
"str": [
"abc",
"xyz"
],
"int": [
22,
104
]
}
Option 3
Here's a third option using a dictionary with setdefault (similar to the other answer with a defaultdict):
gps = {}
for s in sample_list:
gps.setdefault(type(s).__name__, []).append(s)
gps
{
"float": [
1.23,
34.34
],
"str": [
"abc",
"xyz"
],
"int": [
22,
104
]
}
If we're talking efficiency, however, the defaultdict approach in the other answer is slightly more efficient compared to dict + setdefault.
You can use defaultdict from collections module:
from collections import defaultdict
sample_list = [1.23, 34.34, 'abc', 'xyz', 22, 104]
final = defaultdict(list)
for elm in sample_list:
final[type(elm).__name__].append(elm)
print(final)
Output:
defaultdict(<type 'list'>, {'int': [22, 104], 'float': [1.23, 34.34], 'str': ['abc', 'xyz']})
And you can convert final into a regular dict:
print(dict(final))
{'int': [22, 104], 'float': [1.23, 34.34], 'str': ['abc', 'xyz']}
I'm trying to use counter on a list of dictionaries in order to count how many time each dictionary repeats itself.
Not all the dictionaries in the list necessarily has the same keys.
lets assume I have the following list:
my_list=({"id":1,"first_name":"Jhon","last_name":"Smith"},{"id":2,"first_name":"Jeff","last_name":"Levi"},{"id":3,"first_name":"Jhon"},{"id":1,"first_name":"Jhon","last_name":"Smith"})
My desired solution is
solution={
{"id":1,"first_name":"Jhon","last_name":"Smith"}:2
{"id":2,"first_name":"Jeff","last_name":"Levi"}:1
{"id":3,"first_name":"Jhon"}}
I have tried
import collections
c=collections.Counter(my_list)
but I get the following error
TypeError: unhashable type: 'dict'
Do you have any suggestion
Thanks
You can't use dictionary as a key in other dictionary. That's why you get a TypeError: unhashable type: 'dict'.
You can serialize the dictionary to a JSON string, which can be used as a dictionary key.
import json
import collections
my_list = [{"id":1,"first_name":"Jhon","last_name":"Smith"},
{"id":2,"first_name":"Jeff","last_name":"Levi"},
{"id":3,"first_name":"Jhon"},
{"id":1,"first_name":"Jhon","last_name":"Smith"}]
c = collections.Counter(json.dumps(l) for l in my_list)
print c
>>> Counter({'{"first_name": "Jhon", "last_name": "Smith", "id": 1}': 2,
'{"first_name": "Jeff", "last_name": "Levi", "id": 2}': 1,
'{"first_name": "Jhon", "id": 3}': 1})
Counter is tool that stores items in a iterable as a dict wherein dict.keys() represent the items and dict.values() represent the count of that item in the iterable.
In a dictionary, however, you cannot have repetitive keys, as the keys must be unique. There is therefore no point in counting anything, since we already know it's 1. On the other hand, there may be repetitive values stored in the dict. For instance:
>>> from collections import Counter
>>> my_dict = {'a': 'me', 'b':'you', 'c':'me', 'd':'me'}
>>> Counter(my_dict) # As plain dict.
Counter({'b': 'you', 'a': 'me', 'c': 'me', 'd': 'me'})
>>> Counter(my_dict.values()) # As dict values.
Counter({'me': 3, 'you': 1})
Now let's say we have list of dictionaries, and we want to counter the values in those dictionaries; as is the case in your question:
>>> my_dict = [
... {'age': 30, 'name': 'John'},
... {'age': 20, 'name': 'Jeff'},
... {'age': 30, 'name': 'John'},
... {'age': 25, 'name': 'John'}
... ]
>>> Counter(tuple(i.values()) for i in a) # As a generator of values as tuple.
Counter({(30, 'John'): 2, (25, 'John'): 1, (20, 'Jeff'): 1})
Now you can of course take this tuples and convert them to a dict:
>>> {key: value for key, value in b.items()}
{(25, 'John'): 1, (30, 'John'): 2, (20, 'Jeff'): 1}
or go even further, and use named tuples from collections.namedtuple and identify your tuples by name, to which you can later refer much more easily, and clearly.
Hope this helps.
Learn more about collections.Counter from the documentations or this useful set of examples. You can also refer to Raymond Hettinger (Python's maintainer of collections toolbox) videos on YouTube. He has some great tutorials on different tools.
Unfortunately dict are not hashable. So I write this code. Result is not like your desired solution (because not possible) but may be you can use that.
ids_l = [i['id'] for i in my_list]
ids_s = list(set(ids_l))
#k is basickly [id, how many]
k = [[i,ids_l.count(i)] for i in ids_s]
#finding my_list from id
def finder(x):
for i in my_list:
if i['id'] == x:
return i
res = []
for i in range(len(ids_s)):
#k[i][1] how many value
#finder(k[i][0]) return dict
res.append([k[i][1],finder(k[i][0])])
print(res)
this code return
[
[2, {'id': 1, 'first_name': 'Jhon', 'last_name': 'Smith'}],
[1, {'id': 2, 'first_name': 'Jeff', 'last_name': 'Levi'}],
[1, {'id': 3, 'first_name': 'Jhon'}]
]
ps: sorry my poor english