I have the following list:
["stephane", "philippe", "hélène", ["hugo", "jean-michel", "fernand"], "gustave"]
And I would like to order it like this:
["gustave", "hélène", ["fernand", "hugo", "jean-michel"], "philippe", "stephane"]
NB: If there is a nested list following a user, this list must stay to the right of this user.
In addition to that all nested lists works the same way. It's recursive.
Your data sounds like it would be better represented as a dictionary. Lists where consecutive elements have a special relationship sound odd.
If you instead represented your data like this:
{
"stephane": {},
"philippe": {},
"hélène": {
"hugo": {},
"jean-michel": {},
"fernand": {},
},
"gustave": {},
}
Then you can simply sort the keys of the dictionaries to get the order you want.
I've used Ned's proposal and came up with this:
d = {
"stephane": {},
"philippe": {},
"helene": {
"hugo": {},
"jean-michel": {},
"fernand": {},
},
"gustave": {},
}
def sort_dict_as_list(d):
sorted_list = []
for k, v in sorted(d.items()):
if k:
sorted_list.append(k)
if v:
sorted_list.append(v)
return sorted_list
def sort_recursive(d):
if d:
for k, v in d.items():
d[k] = sort_recursive(v)
return sort_dict_as_list(d)
else:
return d
if __name__ == "__main__":
print sort_recursive(d)
Output
python sortit.py
['gustave', 'helene', ['fernand', 'hugo', 'jean-michel'], 'philippe', 'stephane']
I haven't tested it thoroughly, but it's a starting point. I was trying to solve it with a list as a data structure, but I ended up nesting recursive functions and it was way too ugly... Ned's proposal was really good.
Related
I have a list of objects, like so:
[
{"title":"cdap_tests", "datacenter":"B1", "count_failed": 1},
{"title":"cdap_tests", "datacenter":"G1", "count_failed": 1},
{"title":"cdap_tests", "datacenter":"GOV1", "count_failed": 1},
{"title":"developer_portal_tests", "datacenter":"B1", "count_failed": 1}
]
and I want to combine the objects that have the same title attribute together like so:
[
{"title":"cdap_tests", "datacenter":"B1,G1,GOV1", "count_failed": 1},
{"title":"developer_portal_tests", "datacenter":"B1", "count_failed": 1}
]
I have tried comparing each one to another based on their attribute, and adding the string to the other string if they were the same, but for some reason it is not combining them, I simply get the same data back from the function return
new_data_list = []
for row_to_compare_to in data:
for row_to_compare_from in data:
if row_to_compare_from["datacenter"] == row_to_compare_to["datacenter"]:
pass
elif row_to_compare_from["title"] == row_to_compare_to["title"]:
row_to_compare_to["datacenter"] = f"{row_to_compare_from['datacenter']}, {row_to_compare_to['datacenter']}"
row_to_compare_to["count_failed"] = f"{row_to_compare_from['count_failed']}, {row_to_compare_to['count_failed']}"
new_data_list.append(row_to_compare_to)
return new_data_list
Could someone point me in the direction of what I am doing wrong? Or maybe a cleaner solution?
The code produces an error because the "count_failed" is not in every dictionary.
If I were starting from scratch, I might substitute the original data list for a dictionary of dictionaries where the key to the outer dictionary is the title of each entry. This would result in code that is easier to read. I might also make the appended data like "B1,G1,GOV1" a list instead ["B1", "G1", "GOV1"].
I'm sure my approach isn't the most efficient or Pythonic, but I believe it works:
new_data_list = []
for raw_dct in data:
if raw_dct['title'] in [dct['title'] for dct in new_data_list]:
for new_dct in new_data_list:
if raw_dct['title'] == new_dct['title']:
for k, v in raw_dct.items():
if k in new_dct.keys():
if type(v) is str:
if v not in new_dct[k]:
new_dct[k] += "," + str(v)
if type(v) is int:
new_dct[k] += v
else:
new_dct[k] = v
else:
new_data_list.append(raw_dct)
which gives your desired new_data_list that also takes into account the counting of integer attributes like "count_failed".
I have the following piece of code:
payload = [
{
'car': {
'vin': message.car_reference.vin,
'brand': message.car_reference.model_manufacturer,
'model': message.car_reference.model_description,
'color': message.car_reference.color,
},
}
]
The only field on message.car_reference that is guaranteed to not be None is vin.
I still want the other keys (brand, model, color) to be in the dict only if they have a value.
The payload gets send to an external API that gives me an error if e.g. color = None.
How do I make it so that keys and values are only added, if their value is not None?
What came to my mind until now was mutlitple if-statements, but that looks awful and I don't think it's the right way.
This code recursively looks inside the data structure
def recur_remover(collection):
if isinstance(collection, list):
# This allows you to pass in the whole list immediately
for item in collection:
recur_remover(item)
elif isinstance(collection, dict):
# When you hit a dictionary, this checks if there are nested dictionaries
to_delete = []
for key, val in collection.items():
if val is None:
to_delete.append(key)
else:
recur_remover(collection[key])
for k in to_delete:
# deletes all unwanted keys at once instead of mutating the dict each time
del collection[k]
else:
return
If I understand your problem correctly, you may do this
your_car_collection = [{'car': {k: v for k, v in car['car'].items() if v}} for car in your_car_collection]
I have a dynamically growing list of arrays that I would like to add like values together. Here's an example:
{"something" : [{"one":"200"}, {"three":"400"}, {"one":"100"}, {"two":"800"} ... ]}
I'd like to be able to add together the dictionaries inside the list. So, in this case for the key "something", the result would be:
["one":400, "three": 400, "two": 800]
or something to that effect. I'm familiar with the Python's collection counter, but since the "something" list contains dicts, it will not work (unless I'm missing something). The dict is also being dynamically created, so I can't build the list without the dicts. EG:
Counter({'b':3, 'c':4, 'd':5, 'b':2})
Would normally work, but as soon as I try to add an element, the previous value will be overwritten. I've noticed other questions such as these:
Is there any pythonic way to combine two dicts (adding values for keys that appear in both)?
Python count of items in a dictionary of lists
But again, the objects within the list are dicts.
I think this does what you want, but I'm not sure because I don't know what "The dict is also being dynamically created, so I can't build the list without the dicts" means. Still:
input = {
"something" : [{"one":"200"}, {"three":"400"}, {"one":"100"}, {"two":"800"}],
"foo" : [{"a" : 100, "b" : 200}, {"a" : 300, "b": 400}],
}
def counterize(x):
return Counter({k : int(v) for k, v in x.iteritems()})
counts = {
k : sum((counterize(x) for x in v), Counter())
for k, v in input.iteritems()
}
Result:
{
'foo': Counter({'b': 600, 'a': 400}),
'something': Counter({'two': 800, 'three': 400, 'one': 300})
}
I expect using sum with Counter is inefficient (in the same way that using sum with strings is so inefficient that Guido banned it), but I might be wrong. Anyway, if you have performance problems, you could write a function that creates a Counter and repeatedly calls += or update on it:
def makeints(x):
return {k : int(v) for k, v in x.iteritems()}
def total(seq):
result = Counter()
for s in seq:
result.update(s)
return result
counts = {k : total(makeints(x) for x in v) for k, v in input.iteritems()}
One way would be do as follows:
from collections import defaultdict
d = {"something" :
[{"one":"200"}, {"three":"400"}, {"one":"100"}, {"two":"800"}]}
dd = defaultdict(list)
# first get and group values from the original data structure
# and change strings to ints
for inner_dict in d['something']:
for k,v in inner_dict.items():
dd[k].append(int(v))
# second. create output dictionary by summing grouped elemetns
# from the first step.
out_dict = {k:sum(v) for k,v in dd.items()}
print(out_dict)
# {'two': 800, 'one': 300, 'three': 400}
In here I don't use counter, but defaultdict. Its a two step approach.
Having trouble turning these loops into a dictionary comprehension - it might be impossible.
The general idea is that I have a dictionary of excludes that looks like this:
excludes = {
"thing1": ["name", "address"],
"thing2": ["username"]
}
Then I have a larger dictionary that I want to "clean" using the exclusions
original_dict = {
"thing1": {"name": "John", "address": "123 Anywhere Drive", "occupation": "teacher" },
"thing2": {"username": "bearsfan123", "joined_date": "01/01/2015"},
"thing3": {"pet_name": "Spot"}
}
If I run the following:
for k, v in original_dict.iteritems():
if k in excludes.keys():
for key in excludes[k]:
del v[key]
I'm left with:
original_dict = {
"thing1": {"occupation": "teacher" },
"thing2": {"joined_date": "01/01/2015"},
"thing3": {"pet_name": "Spot"}
}
This is perfect, but I'm not sure if I can better represent this as a dictionary comprehension - simply adding the keys I want rather than deleting the ones I don't.
I've gotten down to the second for but am not sure how to represent that in a
new_dict = {k: v for (k, v) in original_dict.iteritems()}
{k:{sub_k:val for sub_k, val in v.iteritems()
if sub_k not in excludes.get(k, {})}
for k,v in original_dict.iteritems()}
Note the need for excludes.get(k, {}).
After pasting in your data and running it in IPython:
In [154]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:{k:{sub_k:val for sub_k, val in v.iteritems()
: if sub_k not in excludes.get(k, {})}
: for k,v in original_dict.iteritems()}
:--
Out[154]:
{'thing1': {'occupation': 'teacher'},
'thing2': {'joined_date': '01/01/2015'},
'thing3': {'pet_name': 'Spot'}}
I'd personally argue that the for-loop approach is more readable and just generally better and less surprising across the spectrum of different developer experience levels of potential code readers.
A slight variation of the for loop approach that doesn't require evil side-effects with del and uses an inner dict comprehension:
new_dict = {}
for k, v in original_dict.iteritems():
k_excludes = excludes.get(k, {})
new_dict[k] = {sub_k:sub_v for sub_k, sub_v in v.iteritems()
if sub_k not in k_excludes}
Suppose I have this:
list = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
I need to find p2 and get its value.
You can try the following ... That will return all the values equivilant to the givenKey in all dictionaries.
ans = [d[key] for d in list if d.has_key(key)]
If this is what your actual code looks like (each key is unique), you should just use one dictionary:
things = { 'p1':'v1', 'p2':'v2', 'p3':'v3' }
do_something(things['p2'])
You can convert a list of dictionaries to one dictionary by merging them with update (but this will overwrite duplicate keys):
dict = {}
for item in list:
dict.update(item)
do_something(dict['p2'])
If that's not possible, you'll need to just loop through them:
for item in list:
if 'p2' in item:
do_something(item['p2'])
If you expect multiple results, you can also build up a list:
p2s = []
for item in list:
if 'p2' in item:
p2s.append(item['p2'])
Also, I wouldn't recommend actually naming any variables dict or list, since that will cause problems with the built-in dict() and list() functions.
These shouldn't be stored in a list to begin with, they should be stored in a dictionary. Since they're stored in a list, though, you can either search them as they are:
lst = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
p2 = next(d["p2"] for d in lst if "p2" in d)
Or turn them into a dictionary:
dct = {}
any(dct.update(d) for d in lst)
p2 = dct["p2"]
You can also use this one-liner:
filter(lambda x: 'p2' in x, list)[0]['p2']
if you have more than one 'p2', this will pick out the first; if you have none, it will raise IndexError.
for d in list:
if d.has_key("p2"):
return d['p2']
If it's a oneoff lookup, you can do something like this
>>> [i['p2'] for i in my_list if 'p2' in i]
['v2']
If you need to look up multiple keys, you should consider converting the list to something that can do key lookups in constant time (such as a dict)
>>> my_list = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
>>> my_dict = dict(i.popitem() for i in my_list)
>>> my_dict['p2']
'v2'
Start by flattening the list of dictionaries out to a dictionary, then you can index it by key and get the value:
{k:v for x in list for k,v in x.iteritems()}['p2']