create a dict of lists from a string - python

I want to convert a string such as 'a=b,a=c,a=d,b=e' into a dict of lists {'a': ['b', 'c', 'd'], 'b': ['e']} in Python 2.6.
My current solution is this:
def merge(d1, d2):
for k, v in d2.items():
if k in d1:
if type(d1[k]) != type(list()):
d1[k] = list(d1[k])
d1[k].append(v)
else:
d1[k] = list(v)
return d1
record = 'a=b,a=c,a=d,b=e'
print reduce(merge, map(dict,[[x.split('=')] for x in record.split(',')]))
which I'm sure is unnecessarily complicated.
Any better solutions?

d = {}
for i in 'a=b,a=c,a=d,b=e'.split(","):
k,v = i.split("=")
d.setdefault(k,[]).append(v)
print d
or, if you're using python > 2.4, you can use defaultdict
from collections import defaultdict
d = defaultdict(list)
for i in 'a=b,a=c,a=d,b=e'.split(","):
k,v = i.split("=")
d[k].append(v)
print d

>>> result={}
>>> mystr='a=b,a=c,a=d,b=e'
>>> for k, v in [s.split('=') for s in mystr.split(',')]:
... result[k] = result.get(k, []) + [v]
...
>>> result
{'a': ['b', 'c', 'd'], 'b': ['e']}

How about...
STR = "a=c,b=d,a=x,a=b"
d = {} # An empty dictionary to start with.
# We split the string at the commas first, and each substr at the '=' sign
pairs = (subs.split('=') for subs in STR.split(','))
# Now we add each pair to a dictionary of lists.
for key, value in pairs:
d[key] = d.get(key, []) + [value]

Using a regex allow to do the work of two splits in only one:
import re
ch ='a=b,a=c ,a=d, b=e'
dic = {}
for k,v in re.findall('(\w+)=(\w+)\s*(?:,|\Z)',ch):
dic.setdefault(k,[]).append(v)
print dic

Related

Return key value pair if value exists in dictionary Python

I have the following dictionary and list:
dict_ = {'one':['a','b','c','d','e','f'],
'two':['g','h','l','m'],
'three':['x','y','z'],
'four':['xx','yy']}
ls = ['a','x','c','h','l']
I need to loop through the dict values and check if the items in ls exist in dict values, if so I would like to return a new dict with key/pair values, as follows:
new_dict = {'one':['a','c'],'two':['h','l'], 'three':['x']}
You can check overlaps with a set intersection -
new_dict = {k: list(set(v) & set(ls)) for k, v in dct.items() if (set(v) & set(ls))}
Output
{'one': ['c', 'a'], 'two': ['l', 'h'], 'three': ['x']}
_dict = {'one':['a','b','c','d','e','f'],
'two':['g','h','l','m'],
'three':['x','y','z']}
ls = ['a','x','c','h','l']
new_dict = {k:[v for v in l if v in ls] for k,l in _dict.items()}
print(new_dict)
First, a tip: you should never call variablke as "orange names" like int, float, dict, input, exc...
The solution:
dictt = {'one':['a','b','c','d','e','f'],
'two':['g','h','l','m'],
'three':['x','y','z']}
ls = ['a','x','c','h','l']
for k, v in dictt.items():
dictt[k] = [el for el in v if el in ls]

python: directory with values other then occurrence of keys

How can I make a directory have values count from a list that is associated with the Keys, opposed to the occurrences of a particular Key.
EXAMPLE: I have two lists, One is of strings, and one is of a values for each string.
say:
strings = ['me', 'you', 'me', 'her','her']
values = [1,20,6,35,5]
and I want:
directory = {'me':7, 'you':20, 'her':40}
This is how I have been making directories:
d = {}
for i in list:
if i in d:
d[i] = d[i]+1
else:
d[i] = 1
valueKeys = zip(d.values(),d.keys())
value = d.values()
keys = d.keys()
>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> for x, y in zip(strings, values):
... d[x] += y
...
>>> d
defaultdict(<class 'int'>, {'you': 20, 'her': 40, 'me': 7})
The resulted defaultdict object can act as plain dict:
for key in d:
print(key, d[key])
Output:
me 7
her 40
you 20
#x=numbers
#list = names
d = {}
for i in list:
if i in d:
d[i] = d[i]+x[list.index(i)]
else:
d[i] = x[list.index(i)]
valueKeys = zip(d.values(),d.keys())
value = d.values()
keys = d.keys()
This was how I solved it

Number of different values assoicated with a key in a list of dicts

Given a list of dictionaries ( each of which have same keys), I want total number of different values with which a given key is associated
$ li = [{1:2,2:3},{1:2,2:4}] $ the expected output is {1:1,2:2}
I came up with the following piece of code...Is there a better way of doing this ?
counts = {}
values = {}
for i in li:
for key,item in i.items():
try:
if item in values[key]:
continue
except KeyError:
else:
try:
counts[key] += 1
except KeyError:
counts[key] = 1
try:
values[key].append(item)
except KeyError:
values[key] = [item]
Something like this is probably more direct:
from collections import defaultdict
counts = defaultdict(set)
for mydict in li:
for k, v in mydict.items():
counts[k].add(v)
That takes care of the collecting / counting of the values. To display them like you want them, this would get you there:
print dict((k, len(v)) for k, v in counts.items())
# prints {1: 1, 2: 2}
Here is yet another alternative:
from collections import defaultdict
counts = defaultdict(int)
for k, v in set(pair for d in li for pair in d.items()):
counts[k] += 1
And the result:
>>> counts
defaultdict(<type 'int'>, {1: 1, 2: 2})
You could so something like this:
li = [{1:2,2:3},{1:2,2:4}]
def makesets(x, y):
for k, v in x.iteritems():
v.add(y[k])
return x
distinctValues = reduce(makesets, li, dict((k, set()) for k in li[0].keys()))
counts = dict((k, len(v)) for k, v in distinctValues.iteritems())
print counts
When I run this it prints:
{1: 1, 2: 2}
which is the desired result.
counts = {}
values = {}
for i in li:
for key,item in i.items():
if not (key in values.keys()):
values[key] = set()
values[key].add(item)
for key in values.keys():
counts[key] = len(values[key])
using flattening list in case dicts are not alway same length:
li=[{1: 2, 2: 3}, {1: 2, 2: 4}, {1: 3}]
dic={}
for i,j in [item for sublist in li for item in sublist.items()]:
dic[i] = dic[i]+1 if i in dic else 1

Convert a list into a nested dictionary

For example I have
x = ['a','b','c']
I need to convert it to:
y['a']['b']['c'] = ''
Is that possible?
For the background, I have a config file which contains dotted notation that points to a place in some json data. I'd like to use the dotted notation string to access that specific data in the json file. For example, in the config:
path_to_data = "user.name.first_name"
I'd like my script to recognize that as:
json_data["user"]["name"]["first_name"]
so I can get the value of the first_name field. I converted the original string into a list, and now I don't know how to convert it to a nested dict.
EDIT: There is an existing data structure that I need to apply the dict with. Let's say:
m = {'a': {'b': {'c': 'lolcat'}}}
so that
m['a']['b']['c']
gives me 'lolcat'. If I get the right dictionary structure (as some of the replies did), I would still need to apply this to the existing dictionary 'm'.
So, again, I get this from a config file:
c = 'a.b.c'
That I converted to a list, thinking this will make things easier:
x = ['a','b','c']
Now I have a json-like data structure:
m = {'a': {'b': {'c': 'lolcat'}}}
So the nested dict generated from 'x' should be able to traverse 'm' so that
m['a']['b']['c']
gets me the cat.
li = ['a','b','c']
d = reduce(lambda x, y: {y:x}, reversed(li+['']))
print(d)
print(d['a']['b']['c'])
I guess you also want to include a value in the end. This works for that too:
def get_value(d, l):
if len(l) > 1:
return get_value(d[l[0]], l[1:])
return d[l[0]]
def add_keys(d, l, c=None):
if len(l) > 1:
d[l[0]] = _d = {}
d[l[0]] = d.get(l[0], {})
add_keys(d[l[0]], l[1:], c)
else:
d[l[0]] = c
def main():
d = {}
l1 = ['a', 'b', 'c', 'd']
c1 = 'letters'
l2 = [42, "42", (42,)]
c2 = 42
add_keys(d, l1, c1)
print d
add_keys(d, l2, c2)
print d
if __name__ == '__main__':
main()
It prints:
{'a': {'b': {'c': {'d': 'letters'}}}}
{'a': {'b': {'c': {'d': 'letters'}}}, 42: {'42': {(42,): 42}}}
letters
42
So it surely works. Recursion for the win.
>>> x = ['a','b','c']
>>> y={}
>>> y[x[-1]]=""
>>> x.pop(-1)
'c'
>>> for i in x[::-1]:
... y={i:y}
...
>>> y
{'a': {'b': {'c': ''}}}
>>> y['a']['b']['c']
''
This will work.
#!/usr/bin/python2
from __future__ import print_function
x = ['a','b','c']
def ltod(l):
rv = d = {}
while l:
i = l.pop(0)
d[i] = {}
d = d[i]
return rv
d = ltod(x)
print(d)
print(d["a"]["b"]["c"])
d["a"]["b"]["c"] = "text"
print(d["a"]["b"]["c"])
Outputs:
{'a': {'b': {'c': {}}}}
{}
text
Find below sample that is not very beautiful but quite simple:
path_to_data = "user.name.first_name"
keys = path_to_data.split('.')
t = []
for key in keys[::-1]: # just to iterate in reversed order
if not t:
t.append({k:{}})
else:
t[-1] = ({k: t[-1]})
#t[0] will contain your dictionary
A general solution would be to use collections.defaultdict to create a nested dictionary. Then override __setitem__ for whatever behavior you'd like. This example will do the string parsing as well.
from collections import defaultdict
class nesteddict(defaultdict):
def __init__(self):
defaultdict.__init__(self, nesteddict)
def __setitem__(self, key, value):
keys = key.split('.')
for key in keys[:-1]:
self = self[key]
defaultdict.__setitem__(self, keys[-1], value)
nd = nesteddict()
nd['a.b.c'] = 'lolcat'
assert nd['a']['b']['c'] == 'lolcat'

pythonic way to filter a dictionary of arrays

I have a dictionary that looks something like this:
d = { 'a':['a','b','c','d'],
'b':['a','b','c','d'],
'c':['a','b','c','d'],
'd':['a','b','c','d'], }
I would like to reduce this dictionary into a new one that contains 2 keys randomly selected from the full set of keys, and also only contains values that correspond to those random keys.
Here is the code I wrote that works, but I feel like there is probably a more pythonic way to do it, any suggestions?
import random
d = { 'a':['a','b','c','d'],
'b':['a','b','c','d'],
'c':['a','b','c','d'],
'd':['a','b','c','d'], }
new_d = {}
r = d.keys()
random.shuffle(r)
r = r[:2]
r_dict = dict( (k,True) for k in r)
for k in r_dict:
a = tuple(d[k])
new_a = []
for item in a:
if item in r_dict:
new_a.append(item)
new_d[k] = new_a
"new_d" has filtered dictionary, for example:
{'a': ['a', 'b'], 'b': ['a', 'b']}
If 'a' and 'b' are the two random keys.
Building on FM's, with the underused set type:
>>> ks = set(random.sample(d, 2))
>>> dict((k, list(ks & set(d[k]))) for k in ks)
{'a': ['a', 'c'], 'c': ['a', 'c']}
How about the following:
import random
rk = random.sample(d.keys(),2)
new_d = {}
for k in rk:
new_d[k] = list(set(d[k]).intersection(rk))
ks = set(random.sample(d.keys(), 2))
nd = dict( (k, list(v for v in d[k] if v in ks)) for k in ks )

Categories

Resources