Problems while zipping two lists as a dictionary? - python

I have the following lists:
a = ['A', 'B', 'C', 'C']
b = ['2', '3', 2, 3]
I am zipping them as follows in order to get a dict:
a_dict = dict(zip(a,b))
However, since the final object is a dict I cant have repeated keys:
{'A': '2', 'B': '3', 'C': 3}
Which alternatives can I have in order to have something like this? (*):
{'A': '2', 'B': '3', 'C':2, 'C': 3}
I tried to convert everything as tuples, however I am using a pop to replace some keys and values from the dictionary:
data['A'] = data.pop('B')
Therefore, I cant use a tuple format. Therefore, given the above two lists, how can I get (*)?

The most common way to resolve key conflicts while still maintaining most of the benefit of the quick indexing of a dict is to turn the values into lists:
d = {}
for k, v in zip(a, b):
d.setdefault(k, []).append(v)
so that d becomes:
{'A': ['2'], 'B': ['3'], 'C': [2, 3]}

Your desired output is not achievable by using dicts. You could either resolve the name conflict by using #blhsing's answer, or use sets to get somewhat close to your desired result as I suspect that you want to check for already existing combinations in a data structure because you tried using tuples.
c = set(zip(a, b))
so c becomes:
{('B', '3'), ('C', 3), ('A', '2'), ('C', 2)}

Or defaultdict of collections:
from collections import defaultdict
d=defaultdict(list)
for k,v in zip(a,b):
d[k].append(v)
And now:
print(dict(d))
Output:
{'A': ['2'], 'B': ['3'], 'C': [2, 3]}
If care about single element lists:
print({k:(v if len(v)-1 else v[0]) for k,v in d.items()})
Output:
{'A': '2', 'B': '3', 'C': [2, 3]}

Related

Adding List Members As a Value To Dictionary

I couldn't find any similar codes so i need your help on my junior question. I summarize the code as follows:
A=[{'a': '1', 'b': '2'}]
L=['x', 'y']
B=[]
for i in A:
for j in L:
i["c"]=j
B.append(i)
print(B)
The output is:
[{'a': '1', 'b': '2', 'c': 'y'}, {'a': '1', 'b': '2', 'c': 'y'}]
What i need is:
[{'a': '1', 'b': '2', 'c': 'x'}, {'a': '1', 'b': '2', 'c': 'y'}]
Thanks for your help.
A more concise, though less readable version:
A = [{'a': '1', 'b': '2'}]
L = ['x', 'y']
B = [ {**z, 'c': j } for j in L for z in A ]
You need to make a copy of the dictionary in your loop, or else it appends the same dictionary twice.
Under the hood, it uses a pointer to the original dictionary. Which means you are editing the same dictionary. When you add it, you are editing the original object, and then saying "This same object is in the list twice.
Here is the code...
import copy
A=[{'a': '1', 'b': '2'}]
L=['x', 'y']
B=[]
for i in A:
for j in L:
i2 = copy.deepcopy(i)
i2["c"]=j
B.append(i2)
print(B)
The key thing is that you need to make sure you're creating a new dictionary rather than modifying the original dictionary in A. Here's a way to do it with zip:
B = [{**a, **c} for a, c in zip(A*2, [{'c': v} for v in L])]
As others have said, you have to copy a dictionary because the variable it is stored to is an object, therefore modifying the object will affect all locations it appears.
a = {'a': 'example'}
B = [a]
#B[0] is a, so modifying a, also updates B[0]
a['a'] = 'test'
print(a) #{'a': 'test'}
print(B[0]) #{'a': 'test'}
I'd like to point out the update function as a possibility.
The update function basically updates the existing dictionary by adding new keys to it from a source dictionary.
Example:
a = {'a': 1, 'b': 2}
c = {'c': 3}
a.update(c) #{'a': 1, 'b': 2, 'c': 3}
Applying it to your code:
A={'a': '1', 'b': '2'}
L=['x', 'y']
import copy
B = []
for j in L:
mod_info = {'c': j}
A.update(mod_info)
B.append(copy.copy(A))
print(B)

Removing inverse duplicates in dictionary python

I have a python dictionary containing some example keys and values:
{'a': ['b'],
'c': ['d'],
'x': ['y'],
'y': ['x'],
'i': ['j','k'],
'j': ['i','k']
'k': ['i','j']}
What letter the key is and which letters are values are irrelevant providing they are shown to have a relationship. I need to be able to remove any 'duplicate' key-value combination so that my dictionary would be displayed as follows.
{'a': ['b'],
'c': ['d'],
'x': ['y'],
'i': ['j','k']}
You can turn each entry to a tuple and use a set to get O(n) time.
d = {'a': ['b'],
'c': ['d'],
'x': ['y'],
'y': ['x'],
'i': ['j','k'],
'j': ['i','k'],
'k': ['i','j']}
seen = set()
to_remove = []
for key, val in d.items():
entry = tuple(sorted(val.copy() + [key]))
to_remove.append(key) if entry in seen else seen.add(entry)
for key in to_remove:
del d[key]
print(d)
Output:
{'a': ['b'], 'c': ['d'], 'x': ['y'], 'i': ['j', 'k']}
Here is a solution how you can have it in one single loop with a dict comprehension as a one liner:
{k: v for i, (k, v) in enumerate(d.items()) if not set(list(d.keys())[:i]).intersection(v)}
And if you want to have it really fast:
s = set()
dmod = {}
for k, v in d.items():
s.add(k)
if not s.intersection(v):
dmod[k] = v
Both approaches assume your dict is named d.
Result:
# {'a': ['b'], 'c': ['d'], 'x': ['y'], 'i': ['j', 'k']}
However, I have to state here that your text description does not suit the expected example. It would be nice if you could update that.
Besides that: are you aware, that the algorithm you ask for is completely order dependent? No solution which returns the result you want will work reliably prior python 3.6 without explicitely using ordered dicts.
I don't know your use case, but is it ok that applying the same algorithm to a e.g. backwards ordered dict creates a different result?
Another one liner:
>>> d = {'a': ['b'], 'c': ['d'], 'x': ['y'], 'y': ['x'], 'i': ['j','k'], 'j': ['i','k'], 'k': ['i','j']}
>>> dict({tuple(sorted((k, *v))):(k, v) for k, v in d.items()}.values())
{'a': ['b'], 'c': ['d'], 'y': ['x'], 'k': ['i', 'j']}
The inner dict is built with the sorted tuples (key, value1, value2, ...) as keys and the (key, [value1, value2, ...]) pairs as values. Obviously, for every sorted tuple, you keep the last (key, [value]) pair (this matters only if the dict keys are ordered, Python >= 3.6). Then build a dict with these (key, [value]) pair.
If you want to get only the first key-value (Python >= 3.6), just reverse the order of iteration of the original dict:
>>> dict({tuple(sorted((k, *v))):(k, v) for k, v in sorted(d.items(), reverse=True)}.values())
{'x': ['y'], 'i': ['j', 'k'], 'c': ['d'], 'a': ['b']}
If that's not clear, here's a more simple example. I want to keep the first list having a given length in a list:
>>> L = [[1], [2], [1,2], [2,3,4], [3], [5,2], [7,8,9]]
>>> {len(v): v for v in reversed(L)}
{3: [2, 3, 4], 2: [1, 2], 1: [1]}
We see that only the first value is kept:
[*[1]*, [2], *[1,2]*, *[2,3,4]*, [3], [5,2], [7,8,9]]
Because this first value is the last to be added to the dict and overwrite the next one (or previous one in reverse order).

how to identify relationship/mapping between the two list in python?

I have created two list.
list1= [a,b,c,a,d]
list2=[1,2,3,4,5]
I want to find relationship between this two list based on index position i.e
In list1 a is repeated 2 times index 0,3 .in list2 index 0,3 values are 1 ,4 the relation is a one to many is a:{1,4}
next b not repeated in list 1 and it index is 1 and list2 index 1 value is 2 ,the relation is one to one b:{2}
my expected output will be {a:{1,4},b:{2},c:{3},d:{5}}
I'd use a defaultdict:
from collections import defaultdict
list1 = ['a', 'b', 'c', 'a', 'd']
list2 = [1, 2, 3, 4, 5]
result = defaultdict(set)
for value1, value2, in zip(list1, list2):
result[value1].add(value2)
print(dict(result))
outputs
{'a': {1, 4}, 'b': {2}, 'c': {3}, 'd': {5}}
You can use a combination of dictionary and list comprehension to do this:
{x: [list2[i] for i, j in enumerate(list1) if j == x] for x in list1}
output:
{'a': [1, 4], 'b': [2], 'c': [3], 'd': [5]}
a = ['a', 'b', 'c', 'a', 'd']
b = [1, 2, 3, 4, 5]
ret = {}
for idx, _a in enumerate(a):
value = ret.get(_a, ret.setdefault(_a, []))
value.append(b[idx])
And ret will be the output
Option is to zip the two lists:
L = list(zip(list1, list2))
Result:
[('a', 1), ('b', 2), ('c', 3), ('a', 4), ('d', 5)]
Use it to create a dictionary with sets as values:
D ={}
for key in L:
if key[0] not in D:
D[key[0]] = {key[1]}
else:
D[key[0]].add(key[1])
I would not do it this way in real code, but this approach is mildly entertaining and perhaps educational.
from collections import defaultdict
from itertools import groupby
from operator import itemgetter
xs = ['a', 'b', 'c', 'a', 'd']
ys = [1, 2, 3, 4, 5]
d = {
x : set(y for _, y in group)
for x, group in groupby(sorted(zip(xs, ys)), key = itemgetter(0))
}
print(d) # {'a': {1, 4}, 'b': {2}, 'c': {3}, 'd': {5}}
It's not from pure python, as this question tagged with pandas I tried this way.
Option-1
df=pd.DataFrame({'l1':list1,'l2':list2})
res1=df.groupby('l1').apply(lambda x:x.l2.values.tolist()).to_dict()
Option-2
print df.groupby('l1')['l2'].unique().to_dict()
Output:
{'a': [1, 4], 'c': [3], 'b': [2], 'd': [5]}

Creating a list of dictionaries in python

I have a following data set that I read in from a text file:
all_examples= ['A,1,1', 'B,2,1', 'C,4,4', 'D,4,5']
I need to create a list of dictionary as follows:
lst = [
{"A":1, "B":2, "C":4, "D":4 },
{"A":1, "B":1, "C":4, "D":5 }
]
I tried using an generator function but it was hard to create a list as such.
attributes = 'A,B,C'
def get_examples():
for value in examples:
yield dict(zip(attributes, value.strip().replace(" ", "").split(',')))
A one liner, just for fun:
all_examples = ['A,1,1', 'B,2,1', 'C,4,4', 'D,4,5']
map(dict, zip(*[[(s[0], int(x)) for x in s.split(',')[1:]] for s in all_examples]))
Produces:
[{'A': 1, 'C': 4, 'B': 2, 'D': 4},
{'A': 1, 'C': 4, 'B': 1, 'D': 5}]
As a bonus, this will work for longer sequences too:
all_examples = ['A,1,1,1', 'B,2,1,2', 'C,4,4,3', 'D,4,5,6']
Output:
[{'A': 1, 'C': 4, 'B': 2, 'D': 4},
{'A': 1, 'C': 4, 'B': 1, 'D': 5},
{'A': 1, 'C': 3, 'B': 2, 'D': 6}]
Explanation:
map(dict, zip(*[[(s[0], int(x)) for x in s.split(',')[1:]] for s in all_examples]))
[... for s in all_examples] For each element in your list:
s.split(',')[1:] Split it by commas, then take each element after the first
(...) for x in and turn it into a list of tuples
s[0], int(x) of the first letter, with that element converted to integer
zip(*[...]) now transpose your lists of tuples
map(dict, ...) and turn each one into a dictionary!
Also just for fun, but with a focus on understandability:
all_examples = ['A,1,1', 'B,2,1', 'C,4,4', 'D,4,5']
ll = [ x.split(",") for x in all_examples ]
ld = list()
for col in range(1, len(ll[0])):
ld.append({ l[0] : int(l[col]) for l in ll })
print ld
will print
[{'A': 1, 'C': 4, 'B': 2, 'D': 4}, {'A': 1, 'C': 4, 'B': 1, 'D': 5}]
Works as long as the input is csv with integers and lines are same length.
Dissection: I will use the teminology "thing" for A, B and C and "measurement" for the "columns" in the data, i.e. those values in the same "csv-column" of the inut data.
Get the string input data into a list for each line: A,1,1 -> ["A","1","1"]
ll = [ x.split(",") for x in all_examples ]
The result is supposed to be a list of dicts, so let's initialize one:
ld = list()
For each measurement (assuming that all lines have the same number of columns):
for col in range(1, len(ll[0])):
Take the thing l[0], e.g. "A", from the line and assign the numeric value int(), e.g. 1, of the measurement in the respective column l[col], e.g. "1", to the thing. Then use a dictionary comprehension to combine it into the next line of the desired result. Finally append() the dict to the result list ld.
ld.append({ l[0] : int(l[col]) for l in ll })
View unfoamtted. Use print json.dumps(ld, indent=4) for more convenient display:
print ld
Hope this helps. Find more on dict comprehensions e.g. here (Python3 version of this great book).
You actually have a list of strings, and you'd like to have a list of paired dictionaries generated from the same key in the tuple triplets of each string.
To keep this relatively simple, I'll use a for loop instead of a complicated dictionary comprehension structure.
my_dictionary_list = list()
d1 = dict()
d2 = dict()
for triplet_str in all_examples:
key, val1, val2 = triplet_str.split(',')
d1[key] = val1
d2[key] = val2
my_dictionary_list.append(d1)
my_dictionary_list.append(d2)
>>> my_dictionary_list
my_dictionary_list
[{'A': '1', 'B': '2', 'C': '4', 'D': '4'},
{'A': '1', 'B': '1', 'C': '4', 'D': '5'}]
Your question should be "How to crate list of dictionaries?". Here's something you would like to consider.
>>> dict={}
>>> dict2={}
>>> new_list = []
>>> all_examples=['A,1,1', 'B,2,1', 'C,4,4', 'D,4,5']
>>> for k in all_examples:
... ele=k.split(",")
... dict[str(ele[0])]=ele[1]
... dict[str(ele[0])]=ele[2]
... new_list.append(dict)
... new_list.append(dict2)
>>> dict
{'A': '1', 'C': '4', 'B': '2', 'D': '4'}
>>> dict2
{'A': '1', 'C': '4', 'B': '1', 'D': '5'}

Swap dictionary keys and values when values are lists

Similarly to this question and this question, I'd like to swap keys and values in a dictionary.
The difference is, my values are lists, not just single values.
Thus, I'd like to turn:
In [120]: swapdict = dict(foo=['a', 'b'], bar=['c', 'd'])
In [121]: swapdict
Out[121]: {'bar': ['c', 'd'], 'foo': ['a', 'b']}
into:
{'a': 'foo', 'b': 'foo', 'c': 'bar', 'd': 'bar'}
Let's assume I'm happy that my values are unique.
You can use a dictionary comprehension and the .items() method.
In []: {k: oldk for oldk, oldv in swapdict.items() for k in oldv}
Out[]: {'a': 'foo', 'b': 'foo', 'c': 'bar', 'd': 'bar'}

Categories

Resources