Replacing multiple occurrences in nested arrays - python

I've got this python dictionary "mydict", containing arrays, here's what it looks like :
mydict = dict(
one=['foo', 'bar', 'foobar', 'barfoo', 'example'],
two=['bar', 'example', 'foobar'],
three=['foo', 'example'])
i'd like to replace all the occurrences of "example" by "someotherword".
While I can already think of a few ways to do it, is there a most "pythonic" method to achieve this ?

for arr in mydict.values():
for i, s in enumerate(arr):
if s == 'example':
arr[i] = 'someotherword'

If you want to leave the original untouched, and just return a new dictionary with the modifications applied, you can use:
replacements = {'example' : 'someotherword'}
newdict = dict((k, [replacements.get(x,x) for x in v])
for (k,v) in mydict.iteritems())
This also has the advantage that its easy to extend with new words just by adding them to the replacements dict. If you want to mutate an existing dict in place, you can use the same approach:
for l in mydict.values():
l[:]=[replacements.get(x,x) for x in l]
However it's probably going to be slower than J.F Sebastian's solution, as it rebuilds the whole list rather than just modifying the changed elements in place.

Here's another take:
for key, val in mydict.items():
mydict[key] = ["someotherword" if x == "example" else x for x in val]
I've found that building lists is very fast, but of course profile if performance is important.

Related

tuple to list conversion within dictionary values (list of lists (and tuples))

I am dealing with a dictionary that is formatted as such:
dic = {'Start': [['Story' , '.']],
'Wonderful': [('thing1',), ["thing1", "and", "thing2"]],
'Amazing': [["The", "thing", "action", "the", "thing"]],
'Fantastic': [['loved'], ['ate'], ['messaged']],
'Example': [['bus'], ['car'], ['truck'], ['pickup']]}
if you notice, in the story key, there is a tuple within a list. I am looking for a way to convert all tuples within the inner lists of each key into lists.
I have tried the following:
for value in dic.values():
for inner in value:
inner = list(inner)
but that does not work and I don't see why. I also tried an if type(inner) = tuple statement to try and convert it only if its a tuple but that is not working either... Any help would be very greatly appreciated.
edit: I am not allowed to import, and only have really learned a basic level of python. A solution that I could understand with that in mind is preferred.
You need to invest some time learning how assignment in Python works.
inner = list(inner) constructs a list (right hand side), then binds the name inner to that new list and then... you do nothing with it.
Fixing your code:
for k, vs in dic.items():
dic[k] = [list(x) if isinstance(x, tuple) else x for x in vs]
You need to update the element by its index
for curr in dic.values():
for i, v in enumerate(curr):
if isinstance(v, tuple):
curr[i] = list(v)
print(dic)
Your title, data and code suggest that you only have tuples and lists there and are willing to run list() on all of them, so here's a short way to convert them all to lists and assign them back into the outer lists (which is what you were missing) (Try it online!):
for value in dic.values():
value[:] = map(list, value)
And a fun way (Try it online!):
for value in dic.values():
for i, [*value[i]] in enumerate(value):
pass

filter items in a python dictionary where keys contain a specific string

I'm a C coder developing something in python. I know how to do the following in C (and hence in C-like logic applied to python), but I'm wondering what the 'Python' way of doing it is.
I have a dictionary d, and I'd like to operate on a subset of the items, only those whose key (string) contains a specific substring.
i.e. the C logic would be:
for key in d:
if filter_string in key:
# do something
else
# do nothing, continue
I'm imagining the python version would be something like
filtered_dict = crazy_python_syntax(d, substring)
for key,value in filtered_dict.iteritems():
# do something
I've found a lot of posts on here regarding filtering dictionaries, but couldn't find one which involved exactly this.
My dictionary is not nested and i'm using python 2.7
How about a dict comprehension:
filtered_dict = {k:v for k,v in d.iteritems() if filter_string in k}
One you see it, it should be self-explanatory, as it reads like English pretty well.
This syntax requires Python 2.7 or greater.
In Python 3, there is only dict.items(), not iteritems() so you would use:
filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}
Go for whatever is most readable and easily maintainable. Just because you can write it out in a single line doesn't mean that you should. Your existing solution is close to what I would use other than I would user iteritems to skip the value lookup, and I hate nested ifs if I can avoid them:
for key, val in d.iteritems():
if filter_string not in key:
continue
# do something
However if you realllly want something to let you iterate through a filtered dict then I would not do the two step process of building the filtered dict and then iterating through it, but instead use a generator, because what is more pythonic (and awesome) than a generator?
First we create our generator, and good design dictates that we make it abstract enough to be reusable:
# The implementation of my generator may look vaguely familiar, no?
def filter_dict(d, filter_string):
for key, val in d.iteritems():
if filter_string not in key:
continue
yield key, val
And then we can use the generator to solve your problem nice and cleanly with simple, understandable code:
for key, val in filter_dict(d, some_string):
# do something
In short: generators are awesome.
You can use the built-in filter function to filter dictionaries, lists, etc. based on specific conditions.
filtered_dict = dict(filter(lambda item: filter_str in item[0], d.items()))
The advantage is that you can use it for different data structures.
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}
Jonathon gave you an approach using dict comprehensions in his answer. Here is an approach that deals with your do something part.
If you want to do something with the values of the dictionary, you don't need a dictionary comprehension at all:
I'm using iteritems() since you tagged your question with python-2.7
results = map(some_function, [(k,v) for k,v in a_dict.iteritems() if 'foo' in k])
Now the result will be in a list with some_function applied to each key/value pair of the dictionary, that has foo in its key.
If you just want to deal with the values and ignore the keys, just change the list comprehension:
results = map(some_function, [v for k,v in a_dict.iteritems() if 'foo' in k])
some_function can be any callable, so a lambda would work as well:
results = map(lambda x: x*2, [v for k,v in a_dict.iteritems() if 'foo' in k])
The inner list is actually not required, as you can pass a generator expression to map as well:
>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]
You can use the built-in function 'filter()':
data = {'aaa':12, 'bbb':23, 'ccc':8, 'ddd':34}
# filter by key
print(dict(filter(lambda e:e[0]=='bbb', data.items() ) ) )
# filter by value
print(dict(filter(lambda e:e[1]>18, data.items() ) ) )
OUTPUT:
{'bbb':23}
{'bbb':23, 'ddd':34}

Slicing a dictionary of list

I have a dictionary of lists, each list greater than 50 items, and to simplify, lets say the dictionary keys are ['a','b','c']. I spend way to long trying to figure out a very pythonic was to sort and slice these lists. What I have so far:
dict = dictionary_of_lists under discussion
[dict[k].sort(reverse=True) for k in dict.keys()]
for k, l in dict.items():
slice = 10 if k in ('a','c') else 20
dict[k] = l[:slice]
I end up with a sorted, and trimmed up list, just like I want. But what I wanted was a one line piece of code like [dict[k].sort(reverse=True) for k in dict.keys()] when I slice against the sorted list. And if someone can figure out how to put the sorting and slicing together, they would be my hero.
UPDATE: First, I like being able to ask somewhat complex questions because they help me learn better coding skills (since I am self taught). So thanks everyone below! My new code:
for c in list_of_categories:
list = [getattr(p,c.name) for p in people if hasattr(p,c.name)]
slice = c.get_slice_value # I added an #property function to a class named `Category`
c.total = sum(sorted(list, reverse=True)[:slice])
List comprehensions with side effects are usually considered bad style. Create a new dict instead:
dct = {k: sorted(l, reverse=True)[:10 if k in ('a','c') else 20]
for k, l in dct.items()}
Also slice values look arbitrary at the moment, it might be better to configure them separately, for example:
slices = {
'a': 10,
'b': 10,
'c': 20
}
dct = {k: sorted(l, reverse=True)[:slices[k]]
for k, l in dct.items()}
sort() works in place, affecting each list. You'd want to create new ones:
[sorted(d[k], reverse = True)[:10 if k in ('a','c') else 20] for k in d.keys()]
Note that it's not very readable.

python how to build a new list from this one

I have a list in the following format:
['CASE_1:a','CASE_1:b','CASE_1:c','CASE_1:d',
'CASE_2:e','CASE_2:f','CASE_2:g','CASE_2:h']
I want to create a new list which looks like like this:
['CASE_1:a,b,c,d','CASE_2:e,f,g,h']
Any idea how to get this done elegantly??
You can use a defaultdict by treating case as the key, and appending to the list each letter, where case and the letter are obtained by splitting the elements of your list on ':' - such as:
from collections import defaultdict
case_letters = defaultdict(list)
start = ['CASE_1:a','CASE_1:b','CASE_1:c','CASE_1:d', 'CASE_2:e','CASE_2:f','CASE_2:g','CASE_2:h']
for el in start:
case, letter = el.split(':')
case_letters[case].append(letter)
result = sorted('{case}:{letters}'.format(case=key, letters=','.join(values)) for key, values in case_letters.iteritems())
print result
As this is homework (edit: or was!!?) - I recommend looking at collections.defaultdict, str.split (and other builtin string methods), at the builtin type list and it's methods (such as append, extend, sort etc...), str.format, the builtin sorted method and generally a dict in general. Use the working example here along with the final manual for reference - all these things will come in handy later on - so it's in your best interest to understand them as best you can.
One other thing to consider is that having something like:
{1: ['a', 'b', 'c', 'd'], 2: ['e', 'f', 'g', 'h']}
is a lot more of a useful format and could be used to recreate your desired list afterwards anyway...
I've deleted my full solution since I realized this is homework, but here's the basic idea:
A dictionary is a better data structure. I would look at a collections.defaultdict. e.g.
yourdict = defaultdict(list)
You can iterate through your list (splitting each element on ':'). Something like:
#only split string once -- resulting in a list of length 2.
case, value = element.split(':',1)
Then you can add these to the dict using the list .append method:
yourdict[case].append(value)
Now, you'll have a dict which maps keys (Case_1, Case_2) to lists (['a','b','c','d'], [...]).
If you really need a list, you can sort the items of the dictionary and join appropriately.
sigh. It looks like the homework tag has been removed (here's my original solution):
from collections import defaultdict
d = defaultdict(list)
for elem in yourlist:
case, value = elem.split(':', 1)
d[case].append(value)
Now you have a dictionary as I described above. If you really want to get your list back:
new_lst = [ case+':'+','.join(values) for case,values in sorted(d.items()) ]
data = ['CASE_1:a','CASE_1:b','CASE_1:c','CASE_1:d', 'CASE_2:e','CASE_2:f','CASE_2:g','CASE_2:h']
output = {}
for item in data:
key, value = item.split(':')
if key not in output:
output[key] = []
output[key].append(value)
result = []
for key, values in output.items():
result.append('%s:%s' % (key, ",".join(values)))
print result
outputs
['CASE_2:e,f,g,h', 'CASE_1:a,b,c,d']
mydict = {}
for item in list:
key,value = item.split(":")
if key in mydict:
mydict[key].append(value)
else:
mydict[key] = [value]
[key + ":" + ",".join(value) for key, value in mydict.iteritems()]
Not much elegance, to be honest. You know, I'd store your list as a dict, cause it behaves as a dict in fact.
output is ['CASE_2:e,f,g,h', 'CASE_1:a,b,c,d']

python - remove dictionary from list if exists

I am trying to remove a dictionary from a list if it already exists but it doesn't seem to be working. Can anyone see what I am doing wrong or advise me what I should be doing
new_dict = {'value': 'some value', 'key': 'someKey'}
if new_dict in my_list:
my_list.remove(new_dict)
new_list is a list of dictionaries where new_dict is definitely in
If new_dict is "definitely" in my_list, then my_list.remove(new_dict) should do the trick (i.e., no need for the if new_dict in my_list, that just slows it down).
my_list = [1,{'value':'some value', 'key' :'somekey'}, 2, {'z':'z', 'x': 'x'}]
new_dict = {'value':'some value', 'key' :'somekey'}
#new_dict = {'z':'z', 'x': 'x'}
differ = 0
matched = 0
for element in my_list:
if type(element) is types.DictType and matched != 0:
differ = 0
# check if dictionary keys match
if element.viewkeys() == new_dict.viewkeys():
# check if dictionary values match
for key in element.keys():
if element[key] != new_dict[key]:
differ = 1
matched = 1
if differ != 1:
my_list.remove(new_dict)
print my_list
It worked for both of the dictionaries for me.
In most cases it is clever to build a new list:
new_list = [ dd for dd in my_list if not dd is new_dict ]
This is typical for a functional programming style, as it avoids side effects. Imagine if you use your solution in a function or method. In most cases you need a modified list only for internal purposes, then modifying an input parameter is dangerous.
Your problem may come from the fact that removing from a list while iterating over the same list is not safe. What you want to do is something like:
copied_list = my_list[:]
if new_dict in copied_list:
my_list.remove(new_dict)
This way, you iterate over a copy of the list and remove from the original.
This may not be the cause of your problem though. It would be interesting to see:
how you build my_list
what you do with my_list after the loop, i.e. how do you realise your dictionary was not removed

Categories

Resources