Converting Keys and Values of the same length into Dictionary form - python

I have a function with two arguments:
keys (i.e. ['a','b','c'])
values to be put into a dictionary (i.e. [1,2,3])
and if the lists are the same length, I put them in dictionary form and return the dictionary. Otherwise, I return the keyword None from the function.
def dict_build(keys,values):
keys = ['']
values = []
dictionary = dict(zip(keys, values))
if len(keys) == len(values):
return dictionary
else:
return None
print(dict_build(['a','b','c'],[1,2,3])== { 'a': 1,'b': 2,'c': 3 })
# False
The output here should be True as the lists have the same length. Where am I going wrong here?

Your function immediately overwrites the keys and values parameters with empty lists, so you always return an empty dictionary.
Just deleting the first two lines of the function should fix it. You could also shorten it down further to just:
def dict_build(keys,values):
if len(keys) == len(values):
return dict(zip(keys, values))
else:
return None

Related

Avoid emptying dictionary recursively

I am trying to create a dictionary from a list recursively and my code only works when there is only one item in the list. It fails for multiple items and I suspect that this is because the dictionary is being recreated through each instance of the recursion instead of adding to it after the first instance. How can I avoid doing this so that the whole list is converted to a dictionary?
Note: the list is a list of tuples containing two items.
def poncePlanner(restaurantChoices):
if len(restaurantChoices) == 0:
return {}
else:
name, resto = restaurantChoices[0][0], restaurantChoices[0][1]
try:
dic[name] = resto
poncePlanner(restaurantChoices[1:])
return dic
except:
dic = {name: resto}
poncePlanner(restaurantChoices[1:])
return dic
Intended input and output:
>>> restaurantChoice = [("Paige", "Dancing Goats"), ("Fareeda", "Botiwala"),
("Ramya", "Minero"), ("Jane", "Pancake Social")]
>>> poncePlanner(restaurantChoice)
{'Jane': 'Pancake Social',
'Ramya': 'Minero',
'Fareeda': 'Botiwala',
'Paige': 'Dancing Goats'}
You have the edge condition, so you need to define what to do when you have more than one. Here you just take the first tuple, make a dict, and then add the results of recursion into that dict:
restaurantChoice = [("Paige", "Dancing Goats"), ("Fareeda", "Botiwala"),
("Ramya", "Minero"), ("Jane", "Pancake Social")]
def poncePlanner(restaurantChoice):
if not restaurantChoice:
return {}
head, *rest = restaurantChoice
return {head[0]: head[1], **poncePlanner(rest)}
poncePlanner(restaurantChoice)
Returning:
{'Jane': 'Pancake Social',
'Ramya': 'Minero',
'Fareeda': 'Botiwala',
'Paige': 'Dancing Goats'}
Since restaurantChoices are already (key,value) pairs, you can simply use the built-in function dict to create the dictionary:
def poncePlanner(restaurantChoices):
return dict(restaurantChoices)
Without built-in functions, you can also use a simple for-loop to return the desired transformation:
def poncePlanner(restaurantChoices):
result = {}
for key,value in restaurantChoices:
result[key] = value
return result
If you really want recursion, I would do something like this, but it doesn't make sense because the lists are not nested:
def poncePlanner(restaurantChoices):
def recursion(i, result):
if i<len(restaurantChoices):
key, value = restaurantChoices[i]
result[key] = value
recursion(i+1, result)
return result
return recursion(0,{})
This recursive function has O(1) time/space complexity per call, so it runs with optimal efficiency.
The main problem with the original code is that the dictionary dic is not passed to the deeper recursion calls, so the new contents are never added to the final dictionary. (They contents added to new dictionaries and forgotten).

How to find repeats or duplicates in nested dictionary?

I have a nested dictionary and I'm trying to find duplicates within in. For example, if I have:
dictionary = {'hello': 3 , 'world':{'this': 5 , 'is':{'a': 3, 'dict': None}}}
The return value would be something like:
True
because this dictionary contains duplicates.
I was able to do this quite easily with a regular dictionary, and I thought this would work well with this case too:
dictionary = {'hello': 3 , 'world':{'this': 5 , 'is':{'a': 3, 'dict': None}}}
rev_dictionary = {}
for key, value in dictionary.items():
rev_dictionary.setdefault(value, set()).add(key)
print(rev_dictionary)
for key,values in dictionary.items():
if len(values) > 1:
values = True
else:
values = False
which throws the following error:
TypeError: unhashable type: 'dict'
How can I get this working?
Thanks for the help!
Note: I'd prefer a solution without using libraries if possible
I am assuming you are defining duplicates by value and not by keys. In that case, you can flatten the nested dict using (mentioned here)
def flatten(d):
out = {}
for key, val in d.items():
if isinstance(val, dict):
val = [val]
if isinstance(val, list):
for subdict in val:
deeper = flatten(subdict).items()
out.update({key + '_' + key2: val2 for key2, val2 in deeper})
else:
out[key] = val
return out
and then check for the condition
v = flatten(d).values()
len(set(v))!=len(v)
results in True
I wrote a simple solution:
dictionary = {'hello': 3 , 'world':{'this': 5 , 'is':{'a': 3, 'dict': None}}}
def get_dups(a, values=None):
if values is None: values = []
if (a in values): return True
values.append(a)
if type(a) == dict:
for i in a.values():
if (get_dups(i, values=values)):
return True
return False
print(get_dups(dictionary))
How it works
We start by saving every value in a list, which we will pass into the function.
Each run we check whether our current value is in that list, and return True once there is a duplicate.
if (a in values): return True
Next we just loop through the values and run get_dups on them if the current index is also a dictionary.
You can recursively add item values of sub-dicts to a set, and if any item value is already "seen" in the set, raise an exception so that the wrapper can return True to indicate that a dupe is found:
def has_dupes(d):
def values(d):
seen = set()
for k, v in d.items():
if isinstance(v, dict):
s = values(v)
if seen & s:
raise RuntimeError()
seen.update(s)
else:
if v in seen:
raise RuntimeError()
seen.add(v)
return seen
try:
values(d)
except RuntimeError:
return True
return False
so that given your sample input, has_dupes(dictionary) would return: True
I think all you need is to flatten the dict before passing to your duplication-detection pipeline:
import pandas as pd
def flatten_dict(d):
df = pd.io.json.json_normalize(d, sep='_')
return df.to_dict(orient='records')[0]
dictionary = {'hello': 3 , 'world':{'this': 5 , 'is':{'a': 3, 'dict': None}}}
dictionary = flatten_dict(dictionary)
print('flattend')
print(dictionary)
rev_dictionary = {}
for key, value in dictionary.items():
rev_dictionary.setdefault(value, set()).add(key)
print('reversed')
print(rev_dictionary)
is_duplicate = False
for key, values in rev_dictionary.items():
if len(values) > 1:
is_duplicate = True
break
print('is duplicate?', is_duplicate)
The result:
flattend
{'hello': 3, 'world_is_a': 3, 'world_is_dict': None, 'world_this': 5}
reversed
{3: {'hello', 'world_is_a'}, None: {'world_is_dict'}, 5: {'world_this'}}
is duplicate? True
Code for flattening a dict borrowed from: Flatten nested Python dictionaries, compressing keys.
Convert the nested dictionary into nested lists of their values:
def nested_values(v):
return map(nested_values, v.values()) if isinstance(v, dict) else v
Then flatten the nested lists into one list of all values in the dictionaries,
and then check the flattened list of values for duplicates:
from itertools import chain
def is_duplicated_value(d):
flat = list(chain.from_iterable(nested_values(d)))
return len(flat) != len(set(flat))
Test:
print is_duplicated_value( {1:'a', 2:'b', 3:{1:'c', 2:'a'}} )
print is_duplicated_value( {1:'a', 2:'b', 3:{1:'c', 2:'d'}} )
Outputs:
True
False
Depending on your use and size of dictionaries etc you may want to recast these steps into a recursive function that adds each value to a set checking if each value is in the set before adding and returning True immediately or False if the dictionary is exhausted.
class Duplicated(ValueError): pass
def is_dup(d):
values = set()
def add(v):
if isinstance(v, dict):
map(add, v.values())
else:
if v in values:
raise Duplicated
else:
values.add(v)
try:
add(d)
return False
except Duplicated:
return True
Test:
print is_dup( {1:'a', 2:'b', 3:{1:'c', 2:'a'}} )
print is_dup( {1:'a', 2:'b', 3:{1:'c', 2:'d'}} )
Outputs:
True
False

Finding if there are distinct elements in a python dictionary

I have a python dictionary containing n key-value pairs, out of which n-1 values are identical and 1 is not. I need to find the key of the distinct element.
For example: consider a python list [{a:1},{b:1},{c:2},{d:1}]. I need the to get 'c' as the output.
I can use a for loop to compare consecutive elements and then use two more for loops to compare those elements with the other elements. But is there a more efficient way to go about it or perhaps a built-in function which I am unaware of?
If you have a dictionary you can quickly check and find the first value which is different from the next two values cycling around the keys of your dictionary.
Here's an example:
def find_different(d):
k = d.keys()
for i in xrange(0, len(k)):
if d[k[i]] != d[k[(i+1)%len(k)]] and d[k[i]] != d[k[(i+2)%len(k)]]:
return k[i]
>>> mydict = {'a':1, 'b':1, 'c':2, 'd':1}
>>> find_different(mydict)
'c'
Otherwise, if what you have is a list of single-key dictionaries, then you can do it quite nicely mapping your list with a function which "extracts" the values from your elements, then check each one using the same logic.
Here's another working example:
def find_different(l):
mask = map(lambda x: x[x.keys()[0]], l)
for i in xrange(0, len(l)):
if mask[i] != mask[(i+1)%len(l)] and mask[i] != mask[(i+2)%len(l)]:
return l[i].keys()[0]
>>> mylist = [{'a':1},{'b':1},{'c':2},{'d':1}]
>>> find_different(mylist)
'c'
NOTE: these solutions do not work in Python 3 as the map function doesn't return a list and neither does the .keys() method of dictionaries.
Assuming that your "list of pairs" (actually list of dictionaries, sigh) cannot be changed:
from collections import defaultdict
def get_pair(d):
return (d.keys()[0], d.values()[0])
def extract_unique(l):
d = defaultdict(list)
for key, value in map(get_pair, l):
d[value].append(key)
return filter(lambda (v,l): len(l) == 1, d.items())[0][1]
If you already have your dictionary, then you make a list of all of the keys: key_list = yourDic.keys(). Using that list, you can then loop through your dictionary. This is easier if you know one of the values, but below I assume that you do not.
yourDic = {'a':1, 'b':4, 'c':1, 'd':1, }
key_list = yourDic.keys()
previous_value = yourDic[key_list[0]] # Making it so loop gets past first test
count = 0
for key in key_list:
test_value = yourDic[key]
if (test_value != previous_value) and count == 1: # Checks first key
print key_list[count - 1]
break
elif (test_value != previous_value):
print key
break
else:
previous_value = test_value
count += 1
So, once you find the value that is different, it will print the key. If you want it to print the value, too, you just need a print test_value statement

How do I check value in a nested dictionary in Python?

Suppose we have a list of dictionaries listD where each dictionary is quite deeply nested with more dictionaries. e.g.suppose that the first element of listD is:
listD[0] = {"bar1":{"bar2":{"bar3":1234}}}
Now I want to check if listD[i]["bar1"]["bar2"]["bar3"] == 1234 for all i. For the first element where i = 0, this is easy as we can just use the expression:
listD[0]["bar1"]["bar2"]["bar3"] == 1234
But I cannot simply write a loop like:
for dictelem in listD:
if dictelem["bar1"]["bar2"]["bar3"] == 1234:
print "equals 1234"
This is because some of the dictionary elements of listD might be of the form
listD[i] = {"bar1":{"bar2":"abcd"}} or
listD[i] = {"bar1":{"bar2":None}}
and if I try to access "bar3" when it doesn't exists, an error will be raised.
Right now I am manually specifying in the code to check for the existence of the bar1, bar2 and bar3 keys and whether that are in fact dictionaries or not. But this is really verbose and I'm quite sure there's a simpler way to do it but I can't figure out how.
def dictcheck(d, p, v):
if len(p):
if isinstance(d,dict) and p[0] in d:
return dictcheck(d[p[0]], p[1:], v)
else:
return d == v
You pass one dict d, one path of keys p, and the final value to check v. It will recursively go in the dicts and finally check if the last value is equal to v.
>>> dictcheck({"bar1":{"bar2":{"bar3":1234}}}, ('bar1','bar2','bar3'), 1234)
True
>>> dictcheck({"bar1":1234}, ('bar1','bar2','bar3'), 1234)
False
So, to answer your question (I want to check if listD[i]["bar1"]["bar2"]["bar3"] == 1234 for all i):
all(dictcheck(x, ('bar1','bar2','bar3'), 1234) for x in listD)
just use a try/except block this way:
for dictelem in listD:
try:
if dictelem["bar1"]["bar2"]["bar3"] == 1234:
print "equals 1234"
except TypeError:
pass
When dealing with nested dictionaries, I think of them as a tree where the keys make up the path to the value. With that in mind, I created a non-recursive function, dict_path with takes in a nested dictionary, the key path, and a value in case not found:
def dict_path(dic, path, value_if_not_found=None):
path = path.split('.')
try:
for key in path:
dic = dic[key]
return dic
except (KeyError, TypeError):
return value_if_not_found
listD = [
{"bar1": {"bar2": 'abcd'}},
{"bar1": {"bar2": None}},
{"bar1": {"bar2": {"bar3": 1234}}},
]
for dic in listD:
value = dict_path(dic, 'bar1.bar2.bar3')
if value == 1234:
print 'Equals 1234:', dic
The function keeps traversing the nested dictionary until one of these three conditions occur:
It found the value in question. In this case, return that value
Along the way, the object is a dictionary, but does not have the requested key. In this case, a KeyError is raised
The object is not a dictionary, the TypeError is raised
For case 2 and 3, we simply return value_if_not_found
you can try this
for item in listD:
if item.get("bar1",{}).get("bar2",{}).get("bar3","") == 1234:
print "yeah, gotcha"

search for any string value in an ordered dictionary

I have a nested ordered dictionary i.e. a ordered dictionary, some of whose values are ordered dictionary that contain further ordered dictionaries.
When I use the collections module and use the following call
yOrDict = yaml_ordered_dict.load('ApplyForm.yml')
then I get an ordered dictionary yOrDict from the yml file ApplyForm.yml.
Say that yOrDict has just one key (ApplyForm) and a value which in turn is an ordered dictionary.
Further call gives another ordered dictionary:
yOrDictLevel1 = yOrDict.get('ApplyForm')
say that yOrDictLevel1 has six keys whose values contain ordered dictionaries.
say that at some place one of the ordered dictionary has a value named as Block.
Is there a call/function/method in python by which I can check if Block appears in the top level ordered dictionary - yOrDict ?
i.e. I want to check if the string "Block" is at all there in yOrDict ?
This should do the trick.
def find_value(needle, container):
# Already found the object. Return.
if needle == container:
return True
values = None
if isinstance(container, dict):
values = container.values()
elif hasattr(container, '__iter__'):
values = container.__iter__()
if values is None:
return False
# Check deeper in the container, if needed.
for val in values:
if find_value(needle, val):
return True
# No match found.
return False
Usage:
In [3]: d = { 'test': ['a', 'b', 'c'], 'd1': { 'd2': 'Block', 'a': 'b'} }
In [4]: find_value('Block', d)
Out[4]: True
Edit: testing whether a value contains needle:
def find_value(needle, container):
# Already found the object. Return.
if isinstance(container, basestring) and needle in container:
return True
values = None
if isinstance(container, dict):
values = container.values()
elif hasattr(container, '__iter__'):
values = container.__iter__()
if values is None:
return False
# Check deeper in the container, if needed.
for val in values:
if find_value(needle, val):
return True
# No match found.
return False
if "Block" in yOrDict.get('ApplyForm').values():
print "Found Block value"
It isn't explicitly a method of the higher level meta-dictionary, since it does a get and searches through the values, but its concise and does the trick.
edit response to your comment
if (("Beijing" in val) for key,val in yOrDict.get('ApplyForm')iteritems()):
print "Found Beijing"

Categories