How can I convert the following list of tuples:
t = [("x", "1","11"),("x", "2","22"),("x", "3","33"),
("y", "3","00"),("z", "2","222"), ("z", "3","333")]
to this list of lists with dictionaries?
[["x",{"1":"11","2":"22","3":"33"}],
["y",{"3":"00"}],
["z",{"2":"222","3":"333"}]]
I'm a big fan of list comprehension. Here's a simple solution using it:
keys = set(map(lambda x: x[0], t))
d = [[k, dict([(y, z) for x, y, z in t if x is k])] for k in keys]
Result:
[['y', {'3': '00'}],
['x', {'1': '11', '2': '22', '3': '33'}],
['z', {'2': '222', '3': '333'}]]
This will be slow for larger lists as d is computed in O(N^2) time.
In two steps: create a dictionary and track the order you've seen the first elements, then build a list from that:
order = []
mapping = {}
for outer, inner, value in t:
if outer not in order:
order.append(outer)
mapping.setdefault(outer, {})[inner] = value
result = [(k, mapping[k]) for k in order]
or use a collections.OrderedDict() object to track the order you first saw the outer keys in:
from collections import OrderedDict
mapping = OrderedDict()
for outer, inner, value in t:
mapping.setdefault(outer, {})[inner] = value
result = mapping.items()
If order isn't important, use the first version and remove all references to order (3 lines), and just use mapping.items() at the end.
If your input is always sorted on the first element of each tuple, you can use itertools.groupby():
from itertools import groupby
from operator import itemgetter
result = [(k, {k: v for _, k, v in g}) for k, g in groupby(t, itemgetter(0))]
Demo:
>>> t = [("x", "1","11"),("x", "2","22"),("x", "3","33"),
... ("y", "3","00"),("z", "2","222"), ("z", "3","333")]
>>> order = []
>>> mapping = {}
>>> for outer, inner, value in t:
... if outer not in order:
... order.append(outer)
... mapping.setdefault(outer, {})[inner] = value
...
>>> [(k, mapping[k]) for k in order]
[('x', {'1': '11', '3': '33', '2': '22'}), ('y', {'3': '00'}), ('z', {'3': '333', '2': '222'})]
>>> mapping.items() # ignoring order
[('y', {'3': '00'}), ('x', {'1': '11', '3': '33', '2': '22'}), ('z', {'3': '333', '2': '222'})]
>>> from collections import OrderedDict
>>> mapping = OrderedDict()
>>> for outer, inner, value in t:
... mapping.setdefault(outer, {})[inner] = value
...
>>> mapping.items()
[('x', {'1': '11', '3': '33', '2': '22'}), ('y', {'3': '00'}), ('z', {'3': '333', '2': '222'})]
>>> from itertools import groupby
>>> from operator import itemgetter
>>> [(k, {k: v for _, k, v in g}) for k, g in groupby(t, itemgetter(0))]
[('x', {'1': '11', '3': '33', '2': '22'}), ('y', {'3': '00'}), ('z', {'3': '333', '2': '222'})]
Related
I have a dict like this
b = {'2': ['10', '5', '4'], '4': ['1', '9', '2'], '3': ['90', '87', '77'], '1': ['30']}
I need to compare each value in the list to others and return only the least value in the dict
I have tried
for k,v in b.items():
for r in range(len(v)):
print(min(v[r] + v[r]))
It is giving me a weird output!
This is the output obtained from that code.
0
5
4
1
9
2
0
7
7
0
0
0
0
I need the key and value which has the least value in the entire dict output like this d = {4:[1]}
Ugly one-liner:
b = {'2': ['10', '5', '4'], '4': ['1', '9', '2'], '3': ['90', '87', '77'], '1': ['30']}
result = dict([min(((int(k), [min(map(int, v))]) for k, v in b.items()), key=lambda t: t[1])])
print(result)
Output:
{4: [1]}
Breakdown:
b = {'2': ['10', '5', '4'], '4': ['1', '9', '2'], '3': ['90', '87', '77'], '1': ['30']}
# Generator of each key with its minimal element
# (here the generator would produce the list [(2, [4]), (4, [1]), (3, [77]), (1, [30])])
key_min = ((int(k), [min(map(int, v))]) for k, v in b.items())
# Pick tuple with minimal value
# (here the tuple (4, [1]) from the previous generator)
min_entry = min(key_min, key=lambda t: t[1])
# Make into dict
# (here {4: [1]}; first element of the tuple is the key and second element the value)
result = dict([min_entry])
print(result)
You can do it with a dict-comprehension
{int(key): [min( int(value) for value in value_list)] for key, value_list in b.items()}
If you want a straightforward answer without any confusion
min_list = {}
for k,v in b.items():
min_value = min(v)
min_list[min_value] = k
print({ min_list[min(min_list)]:min(min_list)})
You want the minimum of minimums, or:
min({k: min(b[k], key=int) for k in b}.items(), key=lambda x: x[1])
This returns the tuple ('4', '1').
First, your list is numbers as text. I did not correct that. If you can fix that then you can take off the in() in this code.
for k, v in b.items():
x = int(min(b[k]))
try:
lowVal
except:
lowVal = x
lowKey = k
else:
if x < lowVal:
lowKey = k
lowVal = x
print('{0}: {1}'.format(lowKey, lowVal))
Step through each item in the dict
find the lowest value and make it an int() and set to x for convenience
try to see if this is our first time through, if it is set the key to lowKey and the lowest value in the list to lowVal
Otherwise if lowVal already exists see if the current lowest value in the list is lower than the previous lowest. If it is then set lowKey and lowVal to the current loops values
Print
????
Profit
Edit: a word
What if you have multiple key value pairs with same minimum value?
This solution works fine for that as well.
result={k:min(map(int,v)) for k,v in b.items()}
minVal=min(result.values())
result={k:[minVal] for k in result.iterkeys() if result[k] == minVal}
print(result)
{'4': [1]}
for ex :
b = {'2': ['10', '5', '4'], '4': ['1', '9', '2'], '3': ['90', '1', '77'], '1': ['30']}
Output will be :
{'3': [1], '4': [1]}
I need to convert the above list of tuples to nested dictionary without overwriting the value as below in python
[('a', '1'),
('b', 'true'),
('b', 'none'),
('a', '2'),
('b', 'true'),
('a', '3'),
('b', 'false')]
{'a': {'1' : { 'b' : ('true','none')},
'2' : { 'b' : ('true')},
'3' : { 'b' : ('false')}}}
Converting each tuple into dictionary using
dict()
and merging the dictionary doesn't work. Is there any pythonic way to do this?
Here's one way to do it with collections.defaultdict:
from collections import defaultdict
import pprint
data = [('a', '1'), ('b', 'true'), ('b', 'none'), ('a', '2'), ('b', 'true'), ('a', '3'), ('b', 'false')]
d = defaultdict(lambda: defaultdict(lambda: defaultdict(tuple)))
for i, j in data:
if i == 'a':
p = d[i][j]
else:
p[i] += j,
pprint.pprint(d)
# {'a': {'1': defaultdict(<class 'tuple'>, {'b': ('true', 'none')}),
# '2': defaultdict(<class 'tuple'>, {'b': ('true',)}),
# '3': defaultdict(<class 'tuple'>, {'b': ('false',)})}}
You could also use the dictionary's setdefault method to return default values for new keys, although the defaultdict approach is much cleaner and faster:
c = {}
for i, j in data:
if i == 'a':
q = c.setdefault(i, {}).setdefault(j, {})
else:
q[i] = q.setdefault(i, ()) + (j,)
pprint.pprint(c)
# {'a': {'1': {'b': ('true', 'none')},
# '2': {'b': ('true',)},
# '3': {'b': ('false',)}}}
Expanding #MosesKoledoye answer, if the first value in the dictionary is only 'a' and 'b', you know that the outer dictionary will always contain at most one element using 'a' as the key and the inner dictionary will always contain at most one element using 'b' as the key. So in the end you get the same information if it is {'1': ('true', 'none')…. You can convert that to your format simply by wrapping the data in some dictionaries. This means you can do the following
output = defaultdict(tuple)
for i, j in data:
if i == 'a':
current = j
else:
# i == 'b'
output[current] += (j, )
This will result in the following:
defaultdict(<type 'tuple'>, {'1': ('true', 'none'), '3': ('false',), '2': ('true',)})
Now to get it into a dictionary like yours you can do the following:
output = {k: {'b': v} for k, v in output.items()}
if output:
output = {'a': output}
Resulting in the following:
{'a': {'1': {'b': ('true', 'none')}, '3': {'b': ('false',)}, '2': {'b': ('true',)}}}
I am trying to generate lists from the elements of a list in python.
For example: there is a list with the following information:
list=['AB4', 'AB3','AC3', 'BC4', 'BC5']
This is the exact format of the elements of the list.
I suppouse to create list for every element, separate for the letters (considering both letters as one block) and separate for the numbers, that will contain the missing character from their string. Here is what I mean:
AB:['4', '3']
AC:['3']
BC:['4', '5']
4:['AB', 'BC']
3:['AB', 'AC']
5:['BC']
These are the lists that I should generate from the original list. There is no limitation to the elements of the original list, and their format is exactly like in the example "two letters and a number".
Thank you in advance.
You can use regexes (the re module) and a defaultdict to accomplish this. The following will work for arbitrary lengths of the non-digit/digit parts of your input strings:
import re
from collections import defaultdict
def str_dig(s): # str_dig('ABC345') -> ('ABC', '345')
return re.match('([^\d]+)(\d+)', s).groups()
lst=['AB4', 'AB3','AC3', 'BC4', 'BC5'] # do NOT shadow list!
d = defaultdict(list)
for x, y in map(str_dig, lst): # map applies the str_dig function to all in lst
d[x].append(y)
d[y].append(x)
# d['AB']: ['4', '3'], d['3']: ['AB', 'AC']
This will do it:
from collections import defaultdict
l=['AB4', 'AB3','AC3', 'BC4', 'BC5']
result=defaultdict(list)
for item in l:
#If you want numbers to be numbers and not strings replace item[2:] with int(item[2:])
result[item[:2]].append(item[2:])
result[item[2:]].append(item[:2])
And you can use this to print it just as you want:
import pprint
pp = pprint.PrettyPrinter()
pp.pprint(result)
output:
{'3': ['AB', 'AC'],
'4': ['AB', 'BC'],
'5': ['BC'],
'AB': ['4', '3'],
'AC': ['3'],
'BC': ['4', '5']}
How about this,
import itertools
import operator
l = ['AB4', 'AB3','AC3', 'BC4', 'BC5']
lists = [(s[:2], s[2]) for s in l] # [('AB', '4'), ('AB', '3'), ('AC', '3'), ('BC', '4'), ('BC', '5')]
results = dict()
for name, group in itertools.groupby(sorted(lists, key=operator.itemgetter(0)), key=operator.itemgetter(0)):
results[name] = map(operator.itemgetter(1), group)
for name, group in itertools.groupby(sorted(lists, key=operator.itemgetter(1)), key=operator.itemgetter(1)):
results[name] = map(operator.itemgetter(0), group)
print(results)
# Output
{ 'AC': ['3'],
'AB': ['4', '3'],
'BC': ['4', '5'],
'3': ['AB', 'AC'],
'5': ['BC'],
'4': ['AB', 'BC']}
I have a list of
[{"1":"value"},{"1":"second_value"},{"2":"third_value"},{"2":"fourth_value"},{"3":"fifth_value"}]
want to convert it into
[{"1":"value","2":"third_value","3":"fifth_value"},{"1":"second_value","2":"fourth_value"}]
There is probably a cleaner way of doing this, input is appreciated:
d = [{"1":"value"},{"1":"second_value"},{"2":"third_value"},{"2":"fourth_value"},{"3":"fifth_value"}]
results = [{}]
for item in stuff:
j,k = item.items()[0] // Do the initial dicts always contain one key-value pair?
for result in results:
if j not in result:
result[j] = k
break
if result == results[-1]:
results.append(item)
break
Result:
[{'1': 'value', '3': 'fifth_value', '2': 'third_value'}, {'1': 'second_value', '2': 'fourth_value'}]
You can use collections.defaultdict:
>>> import collections
>>> result = collections.defaultdict(list)
>>> for item in d:
... result[item.values()[0]].append(item.keys()[0])
...
>>> [{key: value for key in keys} for value, keys in result.items()]
[{'1': 'second_value', '2': 'second_value'}, {'1': 'value', '3': 'value', '2': 'value'}]
Note that second_value comes before value in this as the ordering is rather arbitrary (unless you were to explicitly specify that value should be ordered before second_value the above would give you the ordering that the dictionary returns).
You can use collections.defaultdict here. Iterate over the list, use the values as keys and collect all the keys related to a value in a list.
>>> from collections import defaultdict
>>> d = defaultdict(list)
for dic in lis:
for k, v in dic.items():
d[v].append(k)
...
Now d becomes:
>>> d
defaultdict(<type 'list'>,
{'second_value': ['1', '2'],
'value': ['1', '2', '3']})
Now iterate over d to get the desired result:
>>> [{v1:k for v1 in v} for k, v in d.items()]
[{'1': 'second_value', '2': 'second_value'}, {'1': 'value', '3': 'value', '2': 'value'}]
My Code :
dict( (k,v) if k in ['1','2','3','4'] else (k,None) for k,v in {'1':'one','2':'two'}.items() )
Expected Output :
{'1': 'one', '2': 'two', '3':None, '4':None}
Actual Output :
{'1': 'one', '2': 'two'}
Please help !
The for clause only contains 2 items, so your final dict is only going to have 2 items. The 4-item list is only used as a comparison, not as a source of values to use.
if k in ['1','2','3','4']
is a condition, not an iteration (see the if).
You can write it as:
dict((k, {'1':'one','2':'two'}.get(k, None)) for k in ['1','2','3','4'])
# returns: {'1': 'one', '2': 'two', '3': None, '4': None}
This would give the required result, as you need to traverse the list for 4 elements, and then assign respective values in the dictionary :
theDict = {'1':'one','2':'two'}
s = dict( (k,theDict[k]) if k in theDict else (k,None) for k in ['1','2','3','4'] )
u iterate over a dict with two keys, not a 4-element list
my code:
dict( (k, {'1':'one', '2':'two'}.get(k, None)) for k in ['1', '2','3', '4'])
If my crystal ball wasn't broken, I would guess you want the other direction: iterate k over the list and check if it is in the dictionary.
In this case, you might find useful
l = ['1', '2', '3', '4']
testdict = {'1': 'one', '2': 'two'}
res = dict((k, testdict.get(k, None)) for k in l)
This iterates over l and yields a (k, testdict[k]) tuple if k is in the testdict.
If not, it yields (k, None) - exactly as you (supposedly!) want.