I am working with dictionaries for the first time. I would like to know how I can count how many key value pairs there are in each dictionary where the value is 'available'. I know I probably use len().
seats = {
'A': {'A1':'available', 'A2':'unavailable', 'A3':'available'},
'B': {'B1':'unavailable', 'B2':'available', 'B3':'available'},
'C': {'C1':'available', 'C2':'available', 'C3':'unavailable'},
'D': {'D1':'unavailable', 'D2':'available', 'D3':'available'} }
rowChoice = raw_input('What row? >> ')
numSeats = input('How many Seats? >> ')
I am very new to this, so I really need a very simple method and probably some annotation or explanation how it works.
I'd use the following statement to count each nested dictionary's values:
{k: sum(1 for val in v.itervalues() if val == 'available') for k, v in seats.iteritems()}
This builds a new dictionary from the same keys as seats with each value being the number of seats available. The sum(..) with generator trick efficiently counts all values of the contained per-row dictionary where the value equals 'available'.
Result:
{'A': 2, 'C': 2, 'B': 2, 'D': 2}
To show available seats for a specific row you filter and just use len():
row_available = [k for k, v in seats[rowChoice].iteritems() if v == 'available']
avail_count = len(row_available)
if avail_count:
print 'there {is_are} {count} seat{plural} available in row {rowChoice}, seat{plural} {seats}'.format(
is_are='are' if avail_count > 1 else 'is', count=avail_count,
plural='s' if avail_count > 1 else '', rowChoice=rowChoice,
seats=row_available[0] if avail_count == 1 else ' and '.join([', '.join(row_available[:-1]), row_available[-1]]))
For rowChoice = 'A' this prints:
there are 2 seats available in row A, seats A1 and A3
but it adjusts to form coherent sentences for more or fewer seats too.
Using collections.Counter and itertools.chain:
from collections import Counter
from itertools import chain
print Counter(chain.from_iterable(i.itervalues() for i in seats.itervalues()))
# Counter({'available': 8, 'unavailable': 4})
Related
For example:
m = {'a':1, 'b': 2, 'c': 3}
n = {'a': ('jack', 'true'),
'b': ('tom', 'true'),
'c': ('jack', 'false')}
new_dict = {}
for key, key_info in n.items():
if key in m:
name = key_info[0]
# If name is already in new_dict
if name in new_dict:
if new_dict[name][1] < m[key]:
new_dict[name] = (key, m[key])
else: # Name not in new_dict
new_dict[name] = (key, m[key])
new_dict = dict(new_dict.values())
The output of new_dict would be:
{'c': 3, 'b': 2}
I want to remove elements from 'm' where:
find elements with the same name, which is stored in the 'value' of the 2nd dict 'n'
Because both 'a' & 'c' have the same name 'jack' and c's value 3 is greater than '1', so {'a': 1} is deleted.
My above code works but looks bad. Is there a better way to do it?
Make name-key pairs from n, then sort them according to the key's corresponding value in m:
names_and_keys = [(v[0], k) for k, v in n.items()]
names_and_keys.sort(key=lambda nk: m[nk[1]])
Then build a dict from those pairs. Since they were sorted so that e.g. the c key comes after the a key (since m['c'] > m['a']), 'jack' will be mapped to 'c' (that pair will overwrite the original as each pair is used to build the dict).
lookup = dict(names_and_keys)
Finally, the values of that dict are the keys that should be retained from m (similar to how the original code works, except that we still have to get the values from the original m).
m = {k:m[k] for k in lookup.values()}
The task is to create a list of a random number of dicts (from 2 to 10)
dict's random numbers of keys should be letter, dict's values should
be a number (0-100), example: [{'a': 5, 'b': 7, 'g': 11}, {'a': 3, 'c': 35, 'g': 42}]
get a previously generated list of dicts and create one common dict:
if dicts have same key, we will take max value, and rename key with dict number with max value
if key is only in one dict - take it as is,
example: {'a_1': 5, 'b': 7, 'c': 35, 'g_2': 42}
I've written the following code:
from random import randint, choice
from string import ascii_lowercase
final_dict, indexes_dict = {}, {}
rand_list = [{choice(ascii_lowercase): randint(0, 100) for i in range(len(ascii_lowercase))} for j in range(randint(2, 10))]
for dictionary in rand_list:
for key, value in dictionary.items():
if key not in final_dict:
final_dict.update({key: value}) # add first occurrence
else:
if value < final_dict.get(key):
#TODO indexes_dict.update({:})
continue
else:
final_dict.update({key: value})
#TODO indexes_dict.update({:})
for key in indexes_dict:
final_dict[key + '_' + str(indexes_dict[key])] = final_dict.pop(key)
print(final_dict)
I only need to add some logic in order to keep indexes of final_dict values (created the separated dict for it).
I'm wondering if exists some more Pythonic way in order to solve such tasks.
This approach seems completely reasonable.
I, personally, would probably go around this way, however:
final_dict, tmp_dict = {}, {}
#Transform from list of dicts into dict of lists.
for dictionary in rand_list:
for k, v in dictionary.items():
tmp_dict.setdefault(k, []).append(v)
#Now choose only the biggest one
for k, v in tmp_dict.items():
if len(v) > 1:
final_dict[k+"_"+str(v.index(max(v))+1)] = max(v)
else: final_dict[k] = v[0]
You will need some auxiliary data structure to keep track of unrepeated keys. This uses collections.defaultdict and enumerate to aid the task:
from collections import defaultdict
def merge(dicts):
helper = defaultdict(lambda: [-1, -1, 0]) # key -> max, index_max, count
for i, d in enumerate(dicts, 1): # start indexing at 1
for k, v in d.items():
helper[k][2] += 1 # always increase count
if v > helper[k][0]:
helper[k][:2] = [v, i] # update max and index_max
# build result from helper data structure
result = {}
for k, (max_, index, count) in helper.items():
key = k if count == 1 else "{}_{}".format(k, index)
result[key] = max_
return result
>>> merge([{'a': 5, 'b': 7, 'g': 11}, {'a': 3, 'c': 35, 'g': 42}])
{'a_1': 5, 'b': 7, 'g_2': 42, 'c': 35}
Trying to analyse some strings and compute the number of times they come up. This data is stored in a dictionary. If I were to use the max function only the first highest number encountered would be printed.
count = {"cow": 4, "moo": 4, "sheep": 1}
print(max(count.keys(), key=lambda x: count[x]))
cow
This would yield cow to be the max. How would I get "cow" and "moo" to both be printed
count = {"cow": 4, "moo": 4, "sheep": 1}
cow, moo
Why not keep it simple?
mx = max(count.values())
print([k for k, v in count.items() if v == mx])
# ['cow', 'moo']
The bracketed expression in line two is a list comprehension, essentially a short hand for a for loop that runs over one list-like object (an "iterable") and creates a new list as it goes along. A subtlety in this case is that there are two loop variables (k and v) that run simultaneously their values being assigned by tuple unpacking (.items() returns pairs (key, value) one after the other). To summarize the list comprehension here is roughly equivalent to:
result = []
for k, v in count.items():
if v == mx:
result.append(k)
But the list comprehension will run faster and is also easier to read once you got used to it.
Just group the counts with a defaultdict, and take the maximum:
from collections import defaultdict
count = {"cow": 4, "moo": 4, "sheep": 1}
d = defaultdict(list)
for animal, cnt in count.items():
d[cnt].append(animal)
print(dict(d))
# {4: ['cow', 'moo'], 1: ['sheep']}
print(max(d.items())[1])
# ['cow', 'moo']
This seems like such an obvious thing that I feel like I'm missing out on something, but how do you find out if two different keys in the same dictionary have the exact same value? For example, if you have the dictionary test with the keys a, b, and c and the keys a and b both have the value of 10, how would you figure that out? (For the point of the question, please assume a large number of keys, say 100, and you have no knowledge of how many duplicates there are, if there are multiple sets of duplicates, or if there are duplicates at all). Thanks.
len(dictionary.values()) == len(set(dictionary.values()))
This is under the assumption that the only thing you want to know is if there are any duplicate values, not which values are duplicates, which is what I assumed from your question. Let me know if I misinterpreted the question.
Basically this is just checking if any entries were removed when the values of the dictionary were casted to an object that by definition doesn't have any duplicates.
If the above doesn't work for your purposes, this should be a better solution:
set(k for k,v in d.items() if d.values().count(v) > 1))
Basically the second version just checks to see if there is more than one entry that will be removed if you try popping it out of the list.
To detect all of these cases:
>>> import collections
>>> d = {"a": 10, "b": 15, "c": 10}
>>> value_to_key = collections.defaultdict(list)
>>> for k, v in d.iteritems():
... value_to_key[v].append(k)
...
>>> value_to_key
defaultdict(<type 'list'>, {10: ['a', 'c'], 15: ['b']})
#hivert makes the excellent point that this only works if the values are hashable. If this is not the case, there is no nice O(n) solution(sadly). This is the best I can come up with:
d = {"a": [10, 15], "b": [10, 20], "c": [10, 15]}
values = []
for k, v in d.iteritems():
must_insert = True
for val in values:
if val[0] == v:
val[1].append(k)
must_insert = False
break
if must_insert: values.append([v, [k]])
print [v for v in values if len(v[1]) > 1] #prints [[[10, 15], ['a', 'c']]]
You can tell which are the duplicate values by means of a reverse index - where the key is the duplicate value and the value is the set of keys that have that value (this will work as long as the values in the input dictionary are hashable):
from collections import defaultdict
d = {'w':20, 'x':10, 'y':20, 'z':30, 'a':10}
dd = defaultdict(set)
for k, v in d.items():
dd[v].add(k)
dd = { k : v for k, v in dd.items() if len(v) > 1 }
dd
=> {10: set(['a', 'x']), 20: set(['y', 'w'])}
From that last result it's easy to obtain the set of keys with duplicate values:
set.union(*dd.values())
=> set(['y', 'x', 'a', 'w'])
dico = {'a':0, 'b':0, 'c':1}
result = {}
for val in dico:
if dico[val] in result:
result[dico[val]].append(val)
else:
result[dico[val]] = [val]
>>> result
{0: ['a', 'b'], 1: ['c']}
Then you can filter on the result's key that has a value (list) with more than one element, e.g. a duplicate has been found
Build another dict mapping the values of the first dict to all keys that hold that value:
import collections
inverse_dict = collections.defaultdict(list)
for key in original_dict:
inverse_dict[original_dict[key]].append(key)
keys = set()
for key1 in d:
for key2 in d:
if key1 == key2: continue
if d[key1] == d[key2]:
keys |= {key1, key2}
i.e. that's Θ(n²) what you want. The reason is that a dict does not provide Θ(1) search of a key, given a value. So better rethink your data structure choices if that's not good enough.
You can use list in conjunction with dictionary to find duplicate elements!
Here is a simple code demonstrating the same:
d={"val1":4,"val2":4,"val3":5,"val4":3}
l=[]
for key in d:
l.append(d[key])
l.sort()
print(l)
for i in range(len(l)):
if l[i]==l[i+1]:
print("true, there are duplicate elements.")
print("the keys having duplicate elements are: ")
for key in d:
if d[key]==l[i]:
print(key)
break
output:
runfile('C:/Users/Andromeda/listeqtest.py', wdir='C:/Users/Andromeda')
[3, 4, 4, 5]
true, there are duplicate elements.
the keys having duplicate elements are:
val1
val2
when you sort the elements in the list, you will find that equal values always appear together!
I'm using Python's max function to find the largest integer in a dictionary called count, and the corresponding key (not quite sure if I'm saying it properly; my code probably explains itself better than I'm explaining it). The dictionary count is along the lines of {'a': 100, 'b': 210}, and so on.
number = count[max(count.items(), key=operator.itemgetter(1))[0]]
highest = max(count, key=count.get)
What would I do if there were two equal largest values in there? If I had {'a': 120, 'b': 120, 'c': 100}, this would only find the first of a and b, not both.
Idea is to find max value and get all keys corresponding to that value:
count = {'a': 120, 'b': 120, 'c': 100}
highest = max(count.values())
print([k for k, v in count.items() if v == highest])
Same idea as Asterisk, but without iterating over the list twice. Bit more verbose.
count = { 'a': 120, 'b': 120, 'c': 100 }
answers = []
highest = -1
def f(x):
global highest, answers
if count[x] > highest:
highest = count[x]
answers = [x]
elif count[x] == highest:
answers.append(x)
map(f, count.keys())
print answers
Fast single pass:
a = { 'a': 120, 'b': 120, 'c': 100 }
z = [0]
while a:
key, value = a.popitem()
if value > z[0]:
z = [value,[key]]
elif value == z[0]:
z[1].append(key)
print z
#output:
[120, ['a', 'b']]
And an amusing way with defaultdict:
import collections
b = collections.defaultdict(list)
for key, value in a.iteritems():
b[value].append(key)
print max(b.items())
#output:
(120, ['a', 'b'])
This could be a way (probably not the most efficient).
value = max(count.values())
filter(lambda key: count[key]==value,count)
Sometimes simplest solution may be the best:
max_value = 0
max_keys = []
for k, v in count.items():
if v >= max_value:
if v > max_value:
max_value = v
max_keys = [k]
else:
max_keys.append(k)
print max_keys
The code above is slightly faster than two pass solution like:
highest = max(count.values())
print [k for k,v in count.items() if v == highest]
Of course it's longer, but on the other hand it's very clear and easy to read.
To print a list without bucket. use :
' '.join(map(str, mylist))
or, more verbosely:
' '.join(str(x) for x in mylist)