You may saw this kind of questions a lot, but my demand here is different.
Say I have a dictionary d = {'1': 'stack', '2': 'over', '3': 'flow'}, and I want to make a tuple like t = ('1', 'stack', '2', 'over', '3', 'flow')
What's the simplest way to do it?
You can use itertools.chain. Note that this tuple will not have reproducible order, because dictionaries don't have one:
In [1]: d = {'1': 'stack', '2': 'over', '3': 'flow'}
In [2]: from itertools import chain
In [3]: tuple(chain.from_iterable(d.items()))
Out[3]: ('1', 'stack', '3', 'flow', '2', 'over')
In Python 2.x, you can use d.iteritems() instead of d.items().
Edit: As EdChum notes in the comment, you can ensure the order by applying sorted to the items. Note, however, that by default strings are sorted in lexicographical order:
In [4]: sorted(['2', '10'])
Out[4]: ['10', '2']
You can override this by providing the key parameter to sorted:
In [5]: key = lambda i: (int(i[0]), i[1])
In [6]: tuple(chain.from_iterable(sorted(d.items(), key=key)))
Out[6]: ('1', 'stack', '2', 'over', '3', 'flow')
Related
So I have this tricky dictionary of tuples which I want to filter based on the first occurrence of the informative flag in the value elements. If the flag (which is the element occupying the first position of the tuple) is observed in other keys I will only retain only the first key-value pair in which it occurs and subsequent key-value pairs which contain the flag would be skipped.
old_dict = {'abc':[('abc', '1', '5'), ('def', '1', '5'), ('abcd', '2', '5')],
'def':[('abc', '2', '5'), ('def', '1', '5'), ('abcd', '1', '5')],
'ghi':[('ghi', '1', '5'), ('jkl', '1', '4'), ('mno', '2', '4')]}
I have struggled with a lot of attempts and this latest attempt does not produce anything meaningful.
flgset = set()
new_dict = {}
for elem, tp in old_dict.items():
for flg in tp:
flgset.add(flg[0])
counter = 0
for elem, tp in old_dict.items():
for (item1, item2, item3) in tp:
for flg in flgset:
if flg == item1:
counter = 1
new_dict[elem] = [(item1, item2, item3)]
break
Expected results should be:
new_dict = {'abc':[('abc', '1', '5'), ('def', '1', '5'), ('abcd', '2', '5')],
'ghi':[('ghi', '1', '5'), ('jkl', '1', '4'), ('mno', '2', '4')]}
Thanks in advance.
If i get you correctly, the following should do what you want:
flgset = set()
new_dict = {}
for k, tuple_list in old_dict.items():
# if the key is not in flgset, just keep the k, tuple_list pair
if k not in flgset:
new_dict[k] = tuple_list
# update the elements into flgset
# item in this case is ('abc', '2', '5'),
# since you only want to add the first element, use item[0]
for item in tuple_list:
flgset.add(item[0])
Output as such:
new_dict = {'abc': [('abc', '1', '5'), ('def', '1', '5'), ('abcd', '2', '5')],
'ghi': [('ghi', '1', '5'), ('jkl', '1', '4'), ('mno', '2', '4')]}
flgset = {'abc', 'abcd', 'def', 'ghi', 'jkl', 'mno'}
Others may have more efficient ways to do this, but here's one solution that incorporates your intuitions that you need to loop over old_dict items and use a set:
for key, val in old_dict.items():
if val[0][0] not in set([v[0][0] for v in new_dict.values()]):
new_dict.update({key: val})
Here's a brief explanation of what's going on: First, val[0][0] is the "informative flag" from your dictionary entry (i.e. the first item of the first tuple in the entry list). set([v[0][0] for v in new_dict.values()]) will give you the unique values of that flag in your new dictionary. The inner part is a list comprehension to get all the "flags" and then set will give a unique list. The last line just uses the update method to append to it.
REVISED ANSWER
#VinayPai raises two important issues below in the comments. First, this code is inefficient because it reconstructs the test set each time. Here's the more efficient way he suggests:
flag_list = set()
for key, val in old_dict.items():
if val[0][0] not in flag_list:
new_dict.update({key: val})
flag_list.add(val[0][0])
The second issue is that this will produce inconsistent results because dictionaries are not ordered. One possible solution is to use an OrderedDict. But as #SyntaxVoid suggests, this is only necessary if you're using Python3.5 or earlier (here is a great answer discussing the change). If you can create your data in this fashion, it would solve the problem:
from collections import OrderedDict
old_dict = OrderedDict{'abc':[('abc', '1', '5'), ('def', '1', '5'), ('abcd', '2', '5')],
'def':[('abc', '2', '5'), ('def', '1', '5'), ('abcd', '1', '5')],
'ghi':[('ghi', '1', '5'), ('jkl', '1', '4'), ('mno', '2', '4')]}
I have a set of tuples:
users = set(("test#a.com","password"),("test#b.com","password"))
but could be simplified to a set...and a list of tuples:
licences = [("test#a.com","22"),("test#a.com","23"),("test#b.com","12")]
For every entry of the list the username could be repeated with different "licence" values.
I need to build a list of dictionaries like this:
[{"user":"test#a.com", "licences":["22","23"]},{"user":"test#b.com", "licences":["12"]}]
What I've done so far is this:
licenzadiz = []
for num,user in enumerate(users):
licenzadiz.append({'user': user[0], 'licences': []})
for num2,licence in enumerate(licences):
if user[0] == licence[0]:
licenzadiz[num]['licences'].append(licence[1])
that is working well. BUT I wonder if there are more elegant solutions to my problem.
You can get fancy with nested default dicts:
from collections import defaultdict
items = [('A','1'),('A','3'),('A','2'),
('B','0'),('B','4'),('B','-1'),
('C','7'),('C','6'),('C','12')]
d = defaultdict(lambda: defaultdict(list))
for use,lic in items:
d[use]['username'] = use #<-- Overwrites each time an already known key is found, but thats ok
d[use]['licence'].append(lic)
#Just for printout
for use in d:
print d[use]
print d[use]['username']
print d[use]['licence']
Output:
defaultdict(<type 'list'>, {'username': 'A', 'licence': ['1', '3', '2']})
A
['1', '3', '2']
defaultdict(<type 'list'>, {'username': 'C', 'licence': ['7', '6', '12']})
C
['7', '6', '12']
defaultdict(<type 'list'>, {'username': 'B', 'licence': ['0', '4', '-1']})
B
['0', '4', '-1']
data = {}
for num2,(email, license) in enumerate(licenze):
data.setdefault(email,[]).append(license)
print data #dictionary of email:[licenses,..]
#or
print data.items() # if you want a list
I guess ... i think
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 dictionary I would like to find the minimum key where value[1] is equal to a a specified string.
somedict = {'1': ['110', 'A'], '3': ['1', 'A'], '2': ['3', 'B'], '4': ['1', 'B']}
mindict = min(somedict.iteritems(), key=itemgetter(0) )
This gives me ('1', ['110', 'A'])
I would like to further filter this by finding the min key where value is 'B'
to give me the result ('2', ['3', 'B'])
How would go about this?
Use a generator expression filtering your items first:
min((i for i in somedict.iteritems() if i[1][-1] == 'B'), key=itemgetter(0))
The generator expression produces elements from somedict.iteritems() where the last entry in the value is equal to 'B'.
Note that there is a risk here that no items match your filter! If that could be the case, make sure you catch the ValueError thrown by min() when passed an empty sequence. If you are using Python 3.4 or newer, you can specify a default to be returned for that case:
min((i for i in somedict.iteritems() if i[1][-1] == 'B'),
key=itemgetter(0), default=())
which would return an empty tuple if no items have a last entry 'B' in their value.
Demo:
>>> from operator import itemgetter
>>> somedict = {'1': ['110', 'A'], '3': ['1', 'A'], '2': ['3', 'B'], '4': ['1', 'B']}
>>> min((i for i in somedict.iteritems() if i[1][-1] == 'B'), key=itemgetter(0))
('2', ['3', 'B'])
Why dictionaries in python appears reversed?
>>> a = {'one': '1', 'two': '2', 'three': '3', 'four': '4'}
>>> a
{'four': '4', 'three': '3', 'two': '2', 'one': '1'}
How can I fix this?
Dictionaries in python (and hash tables in general) are unordered. In python you can use the sort() method on the keys to sort them.
Dictionaries have no intrinsic order. You'll have to either roll your own ordered dict implementation, use an ordered list of tuples or use an existing ordered dict implementation.
Python3.1 has an OrderedDict
>>> from collections import OrderedDict
>>> o=OrderedDict([('one', '1'), ('two', '2'), ('three', '3'), ('four', '4')])
>>> o
OrderedDict([('one', '1'), ('two', '2'), ('three', '3'), ('four', '4')])
>>> for k,v in o.items():
... print (k,v)
...
one 1
two 2
three 3
four 4
Now you know dicts are unordered, here is how to convert them to a list which you can order
>>> a = {'one': '1', 'two': '2', 'three': '3', 'four': '4'}
>>> a
{'four': '4', 'three': '3', 'two': '2', 'one': '1'}
sorted by key
>>> sorted(a.items())
[('four', '4'), ('one', '1'), ('three', '3'), ('two', '2')]
sorted by value
>>> from operator import itemgetter
>>> sorted(a.items(),key=itemgetter(1))
[('one', '1'), ('two', '2'), ('three', '3'), ('four', '4')]
>>>
And what is the "standard order" you would be expecting? It is very much application dependent. A python dictionary doesn't guarantee key ordering anyways.
In any case, you can iterate over a dictionary keys() the way you want.
From the Python Tutorial:
It is best to think of a dictionary as
an unordered set of key: value pairs
And from the Python Standard Library (about dict.items):
CPython implementation detail: Keys
and values are listed in an arbitrary
order which is non-random, varies
across Python implementations, and
depends on the dictionary’s history of
insertions and deletions.
So if you need to process the dict in a certain order, sort the keys or values, e.g.:
>>> sorted(a.keys())
['four', 'one', 'three', 'two']
>>> sorted(a.values())
['1', '2', '3', '4']