Pythonic way of updating value in dictionary - python

I have this current code
lst = [1,2,3,4]
c = dict((el,0) for el in lst)
for key in lst:
c[key] += increase_val(key)
Is there a more pythonic way to do it? Like using map? This code words but i would like probably a one-liner or maybe better way of writing this

In my opinion, that is a very clean, readable way of updating the dictionary in the way you wanted.
However, if you are looking for a one-liner, here's one:
new_dict = {x: y + increase_val(x) for x, y in old_dict.items()}
What's different is that this create's a new dictionary instead of updating the original one. If you want to mutate the dictionary in place, I think the plain old for-loop would be the most readable alternative.

In your case no need of c = dict((el,0) for el in lst) statement, because we create dictionary where value of each key is 0.
and in next for loop you are adding increment value to 0 i.e. 0 + 100 = 100, so need of addition also.
You can write code like:
lst = [1,2,3,4]
c = {}
for key in lst:
c[key] = increase_val(key)
collection.Counter()
Use collections.Counter() to remove one iteration over list to create dictionary because default value of every key in your case is 0.
Use Collections library, import collections
Demo:
>>> lst = [1,2,3,4]
>>> data = collections.Counter()
>>> for key in lst:
data[key] += increase_val(key)
collection.defaultdict()
We can use collections.defaultdict also. Just use data = collections.defaultdict(int) in above code. Here default value is zero.
But if we want to set default value to any constant value like 100 then we can use lambda function to set default value to 100
Demo:
>>> data = {}
>>> data["any"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'any'
Get key error because there is on any key in dictionary.
>>> data1 = collections.defaultdict(lambda:0, data)
>>> data1["any"]
0
>>> data1 = collections.defaultdict(lambda:100, data)
>>> data1["any"]
>>> 100

Related

Can someone explain what this does "defaultdict(lambda:0)" [duplicate]

In someone else's code I read the following two lines:
x = defaultdict(lambda: 0)
y = defaultdict(lambda: defaultdict(lambda: 0))
As the argument of defaultdict is a default factory, I think the first line means that when I call x[k] for a nonexistent key k (such as a statement like v=x[k]), the key-value pair (k,0) will be automatically added to the dictionary, as if the statement x[k]=0 is first executed. Am I correct?
And what about y? It seems that the default factory will create a defaultdict with default 0. But what does that mean concretely? I tried to play around with it in Python shell, but couldn't figure out what it is exactly.
I think the first line means that when I call x[k] for a nonexistent key k (such as a statement like v=x[k]), the key-value pair (k,0) will be automatically added to the dictionary, as if the statement x[k]=0 is first executed.
That's right. This is more idiomatically written
x = defaultdict(int)
In the case of y, when you do y["ham"]["spam"], the key "ham" is inserted in y if it does not exist. The value associated with it becomes a defaultdict in which "spam" is automatically inserted with a value of 0.
I.e., y is a kind of "two-tiered" defaultdict. If "ham" not in y, then evaluating y["ham"]["spam"] is like doing
y["ham"] = {}
y["ham"]["spam"] = 0
in terms of ordinary dict.
You are correct for what the first one does. As for y, it will create a defaultdict with default 0 when a key doesn't exist in y, so you can think of this as a nested dictionary. Consider the following example:
y = defaultdict(lambda: defaultdict(lambda: 0))
print y['k1']['k2'] # 0
print dict(y['k1']) # {'k2': 0}
To create an equivalent nested dictionary structure without defaultdict you would need to create an inner dict for y['k1'] and then set y['k1']['k2'] to 0, but defaultdict does all of this behind the scenes when it encounters keys it hasn't seen:
y = {}
y['k1'] = {}
y['k1']['k2'] = 0
The following function may help for playing around with this on an interpreter to better your understanding:
def to_dict(d):
if isinstance(d, defaultdict):
return dict((k, to_dict(v)) for k, v in d.items())
return d
This will return the dict equivalent of a nested defaultdict, which is a lot easier to read, for example:
>>> y = defaultdict(lambda: defaultdict(lambda: 0))
>>> y['a']['b'] = 5
>>> y
defaultdict(<function <lambda> at 0xb7ea93e4>, {'a': defaultdict(<function <lambda> at 0xb7ea9374>, {'b': 5})})
>>> to_dict(y)
{'a': {'b': 5}}
defaultdict takes a zero-argument callable to its constructor, which is called when the key is not found, as you correctly explained.
lambda: 0 will of course always return zero, but the preferred method to do that is defaultdict(int), which will do the same thing.
As for the second part, the author would like to create a new defaultdict(int), or a nested dictionary, whenever a key is not found in the top-level dictionary.
All answers are good enough still I am giving the answer to add more info:
"defaultdict requires an argument that is callable. That return result of that callable object is the default value that the dictionary returns when you try to access the dictionary with a key that does not exist."
Here's an example
SAMPLE= {'Age':28, 'Salary':2000}
SAMPLE = defaultdict(lambda:0,SAMPLE)
>>> SAMPLE
defaultdict(<function <lambda> at 0x0000000002BF7C88>, {'Salary': 2000, 'Age': 28})
>>> SAMPLE['Age']----> This will return 28
>>> SAMPLE['Phone']----> This will return 0 # you got 0 as output for a non existing key inside SAMPLE
y = defaultdict(lambda:defaultdict(lambda:0))
will be helpful if you try this y['a']['b'] += 1

Can I concatenate a value when adding to a dictionary in Python?

dictionary.setdefault(key, []).append(somelist)
instead of appending lists to the value of a certain key I want to concatenate strings to my value in a loop. So each iteration the key would map to a different value that has been concatenated with a new string. How could I do that?
str values are immutable, so you can't concatenate a string to an existing value; you can only replace the original with a new string formed from the old string. A defaultdict simplifies this.
>>> d = collections.defaultdict(str)
>>> d["foo"] += "bar"
>>> d["foo"] += "baz"
>>> d["foo"]
'barbaz'
setdefault doesn't work here because you can't assign to the return value of setdefault, for example
>>> d.setdefault("foo", "") += "bar"
File "<stdin>", line 1
SyntaxError: can't assign to function call
Set an empty string as a default value, and concatenate as usual
from collections import defaultdict
d = defaultdict(str)
d['a'] += 'hello'
d['a'] += 'world'
print(d)
You can use the dict.get method for a default value of an empty string instead:
dictionary[key] = dictionary.get(key, '') + some_string
Can we get desired output so we have clearer picture of what you mean, from what I read you want to concatenate a string to a dictionary value?
dicta = {'vash': 'the'}
string = ' is concatenating'
lista = [' stampede', string]
for i in lista:
for k in dicta:
dicta[k] += i
print(dicta)
Output:
(xenial)vash#localhost:~/python/stack_overflow$ python3.7 conc.py
{'vash': 'the stampede is concatenating'}

access value of a python dict() without knowing the keys

I have a dictionary of a list of dictionaries. something like below:
x = {'a':[{'p':1, 'q':2}, {'p':4, 'q':5}], 'b':[{'p':6, 'q':1}, {'p':10, 'q':12}]}
The length of the lists (values) is the same for all keys of dict x.
I want to get the length of any one value i.e. a list without having to go through the obvious method -> get the keys, use len(x[keys[0]]) to get the length.
my code for this as of now:
val = None
for key in x.keys():
val = x[key]
break
#break after the first iteration as the length of the lists is the same for any key
try:
what_i_Want = len(val)
except TypeError:
print 'val wasn't set'
i am not happy with this, can be made more 'pythonic' i believe.
This is most efficient way, since we don't create any intermediate lists.
print len(x[next(iter(x))]) # 2
Note: For this method to work, the dictionary should have atleast one key in it.
What about this:
val = x[x.keys()[0]]
or alternatively:
val = x.values()[0]
and then your answer is
len(val)
Some of the other solutions (posted by thefourtheye and gnibbler) are better because they are not creating an intermediate list. I added this response merely as an easy to remember and obvious option, not a solution for time-efficient usage.
Works ok in Python2 or Python3
>>> x = {'a':[{'p':1, 'q':2}, {'p':4, 'q':5}], 'b':[{'p':6, 'q':1}, {'p':10, 'q':12}]}
>>> next(len(i) for i in x.values())
2
This is better for Python2 as it avoids making a list of the values. Works well in Python3 too
>>> next(len(x[k]) for k in x)
2
Using next and iter:
>>> x = {'a':[{'p':1, 'q':2}, {'p':4, 'q':5}], 'b':[{'p':6, 'q':1}, {'p':10, 'q':12}]}
>>> val = next(iter(x.values()), None) # Use `itervalues` in Python 2.x
>>> val
[{'q': 2, 'p': 1}, {'q': 5, 'p': 4}]
>>> len(val)
2
>>> x = {}
>>> val = next(iter(x.values()), None) # `None`: default value
>>> val is None
True
>>> x = {'a':[{'p':1, 'q':2}, {'p':4, 'q':5}], 'b':[{'p':6, 'q':1}, {'p':10, 'q':12}]}
>>> len(x.values()[0])
2
Here, x.values gives you a list of all values then you can get length of any one value from it.

How to turn a dictionary "inside-out"

Disclaimer: I am just getting started learning Python
I have a function that counts the number of times a word appears in a text file and sets the word as the key and the count as the value, and stores it in a dictionary "book_index". Here is my code:
alice = open('location of the file', 'r', encoding = "cp1252")
def book_index(alice):
"""Alice is a file reference"""
"""Alice is opened, nothing else is done"""
worddict = {}
line = 0
for ln in alice:
words = ln.split()
for wd in words:
if wd not in worddict:
worddict[wd] = 1 #if wd is not in worddict, increase the count for that word to 1
else:
worddict[wd] = worddict[wd] + 1 #if wd IS in worddict, increase the count for that word BY 1
line = line + 1
return(worddict)
I need to turn that dictionary "inside out" and use the count as the key, and any word that appears x amount of times as the value. For instance: [2, 'hello', 'hi'] where 'hello' and 'hi' appear twice in the text file.
Do I need to loop through my existing dictionary or loop through the text file again?
As a dictionary is a key to value mapping, you cannot efficiently filter by the values. So you will have to loop through all elements in the dictionary to get the keys which values have some specific value.
This will print out all keys in the dictionary d where the value is equal to searchValue:
for k, v in d.items():
if v == searchValue:
print(k)
Regarding your book_index function, note that you can use the built-in Counter for counting things. Counter is essentially a dictionary that works with counts as its values and automatically takes care of nonexistant keys. Using a counter, your code would look like this:
from collections import Counter
def book_index(alice):
worddict = Counter()
for ln in alice:
worddict.update(ln.split())
return worddict
Or, as roippi suggested as a comment to another answer, just worddict = Counter(word for line in alice for word in line.split()).
Personally I would suggest the use of a Counter object here, which is specifically made for this kind of application. For instance:
from collections import Counter
counter = Counter()
for ln in alice:
counter.update(ln.split())
This will give you the relevant dictionary, and if you then read the Counter docs
You can just retrieve the most common results.
This might not work in every case in your proposed problem, but it's slightly nicer than manually iterating through even the first time around.
If you really want to "flip" this dictionary you could do something along these lines:
matching_values = lambda value: (word for word, freq in wordict.items() if freq==value)
{value: matching_values for value in set(worddict.values())}
The above solution has some advantages over other solutions in that the lazy execution means that for very sparse cases where you're not looking to make a lot of calls to this function, or just discover which value actually have corresponding entries, this will be faster as it won't actually iterate through the dictionary.
That said, this solution will usually be worse than the vanilla iteration solution since it actively iterates through the dictionary every time you need a new number.
Not radically different, but I didn't want to just copy the other answers here.
Loop through your existing dictionary, here is an example using dict.setdefault():
countdict = {}
for k, v in worddict.items():
countdict.setdefault(v, []).append(k)
Or with collections.defaultdict:
import collections
countdict = collections.defaultdict(list)
for k, v in worddict.items():
countdict[v].append(k)
Personally I prefer the setdefault() method because the result is a regular dictionary.
Example:
>>> worddict = {"hello": 2, "hi": 2, "world": 4}
>>> countdict = {}
>>> for k, v in worddict.items():
... countdict.setdefault(v, []).append(k)
...
>>> countdict
{2: ['hi', 'hello'], 4: ['world']}
As noted in some of the other answers, you can significantly shorten your book_index function by using collections.Counter.
Without duplicates:
word_by_count_dict = {value: key for key, value in worddict.iteritems()}
See PEP 274 to understand dictionary comprehension with Python: http://www.python.org/dev/peps/pep-0274/
With duplicates:
import collections
words_by_count_dict = collections.defaultdict(list)
for key, value in worddict.iteritems():
words_by_count_dict[value].append(key)
This way:
words_by_count_dict[2] = ["hello", "hi"]

How do I do this with Python list? (itemgetter?)

[{'id':44}, {'name':'alexa'},{'color':'blue'}]
I want to select whatever in the list that is "id".
Basically, I want to print 44, since that's "id" in the list.
That's a weird data structure... A list of one item dictionaries.
key = 'id'
l = [{'id':44}, {'name':'alexa'},{'color':'blue'}]
print [ x[key] for x in l if key in x ][0]
Assuming you can rely on key being present precisely once...
Maybe you should just convert the list into a dictionary first:
key = 'id'
l = [{'id':44}, {'name':'alexa'},{'color':'blue'}]
d = {}
for x in l:
d.update(x)
print d[key]
All the other answers solve your problem, I am just suggesting an alternative way of going about doing this.
Instead of having a list of dicts where you query on the key and have to iterate over all list items to get values, just use a dict of lists. Each key would map to a list of values (or just one value if all your dicts had distinct sets of keys).
So,
data=[{'id':44}, {'name':'alexa'},{'color':'blue'}]
becomes
data={'id':[44], 'name':['alexa'], 'color':['blue']}
and you can neatly access the value for 'id' using data['id'] (or data['id'][0] if you only need one value).
If all your keys are distinct across the dicts (as in your example) you don't even have to have lists of values.
data={'id':44, 'name':'alexa', 'color':'blue'}
Not only does this make your code cleaner, it also speeds up your queries which no longer have to iterate over a list.
Probably this is the best solution:
>>> L = [{'id':44}, {'name':'alexa'},{'color':'blue'}]
>>> newd = {}
>>> for d in L:
... newd.update(d)
>>> newd['id']
44
You could do something like this:
>>> KEY = 'id'
>>>
>>> my_list = [{'id':44}, {'name':'alexa'},{'color':'blue'}]
>>> my_ids = [x[KEY] for x in my_list if KEY in x]
>>> print my_ids
[44]
Which is obviously a list of the values you want. You can then print them as required.
>>> from itertools import dropwhile
>>> def find_value(l, key):
... return dropwhile(lambda x: key not in x, l).next()[key]
>>> find_value([{'id':44}, {'name':'alexa'},{'color':'blue'}], "id")
This will do a linear search, but only until the element is found.
If you want to have proper error handling, use:
def find_value(l, key):
try:
return dropwhile(lambda x: key not in x, l).next()[key]
except StopIteration:
raise ValueError(key)
>>> L = [{'id':44}, {'name':'alexa'},{'color':'blue'}]
>>> newd=dict(d.items()[0] for d in L)
>>> newd['id']
44

Categories