Removing unnecessary list brackets inside a dictionary - python

I have this dictionary:
n ={'b': [['a'], ['c']], 'a': [['c', 'b'], ['c']], 'c': [['b']]}
and require the following output:
n ={'b': ['a', 'c'], 'a': ['c', 'b'], 'c': ['b']}
I tried to use itertools and join but couldn't get it to work, can anyone help out?

Just use chain.from_iterable from itertools to combine these:
from itertools import chain
from_it = chain.from_iterable
{k: list(from_it(i)) for k, i in n.items()}
If you require unique values in the lists (which according to the title you don't), you can additionally wrap the result of from_it in a set.

I would iterate the dict and ignore the irrelevant list.
For uniqueness you can cast each inner_list to a set
n ={'b': [['a', 'b'], ['c']], 'a': [['c', 'b'], ['c']], 'c': [['b']]}
new_n = {}
for k,v in n.items():
n[k] = [inner_item for item in v for inner_item in item]
print (n)

You can try this:
from itertools import chain
n ={'b': [['a'], ['c']], 'a': [['c', 'b'], ['c']], 'c': [['b']]}
new_n = {a:list(set(chain(*[i[0] if len(i) == 1 else i for i in b]))) for a, b in n.items()}
Output:
{'a': ['c', 'b'], 'c': ['b'], 'b': ['a', 'c']}

A one liner solution(and not recommended) to this is :
{key: list(set([item for subarr in value for item in subarr])) for key, value in n.items()}
It is much harder to read though. If you really do not want to import anything, you can write a helper function.
def flat_and_unique_list(list_of_lists):
return list(set([item for sub_list in list_of_lists for item in sub_list]))
{key: flat_and_unique_list(value) for key, value in n.items()}

A solution with sum:
>>> {k: sum(v, []) for k, v in n.items()}
{'a': ['c', 'b', 'c'], 'b': ['a', 'c'], 'c': ['b']}
sum(iterable, start=0, /)
Return the sum of a 'start' value (default: 0) plus an iterable of numbers
Therefore, using an empty list as start value works.
Remove multiplies using without preserving order using set:
>>> {k: list(set(sum(v, []))) for k, v in n.items()}
{'a': ['c', 'b'], 'b': ['a', 'c'], 'c': ['b']}

Related

how to print dictionary key in the even frequency order

I would like print out dictionary key, value pair in the even frequency like
a = dict('A': 3, 'B': 5}
=> ['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
a = dict('A': 4, 'B': 1}
=> ['A', 'B', 'A', 'A', 'A']
I know I can use a while loop to print each key and remove the count every time until all value from all key is 0 but if there is better way to do it?
def func(d: dict):
res = []
while any(i > 0 for i in d.values()):
for k, c in d.items():
if c > 0:
res.append(k)
d[k] -= 1
return res
(I'm assuming you're using a version of Python that guarantees the iteration order of dictionaries)
Here's an itertools-y approach. It creates a generator for each letter that yields the letter the given number of times, and it combines all of them together with zip_longest so they get yielded evenly.
from itertools import repeat, zip_longest
def iterate_evenly(d):
generators = [repeat(k, v) for k,v in d.items()]
exhausted = object()
for round in zip_longest(*generators, fillvalue=exhausted):
for x in round:
if x is not exhausted:
yield x
print(list(iterate_evenly({"A": 3, "B": 5})))
print(list(iterate_evenly({"A": 4, "B": 1})))
Result:
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
['A', 'B', 'A', 'A', 'A']
You can do the same thing in fewer lines, although it becomes harder to read.
from itertools import repeat, zip_longest
def iterate_evenly(d):
exhausted = object()
return [x for round in zip_longest(*(repeat(k, v) for k,v in d.items()), fillvalue=exhausted) for x in round if x is not exhausted]
print(iterate_evenly({"A": 3, "B": 5}))
print(iterate_evenly({"A": 4, "B": 1}))
For a one-liner.
First, create a list with two elements: a list of As and a list of Bs:
>>> d = {'A': 3, 'B': 5}
>>> [[k]*v for k, v in d.items()]
[['A', 'A', 'A'], ['B', 'B', 'B', 'B', 'B']]
[k]*v means: a list with v ks. Second, interleave As and B. We need zip_longest because zip would stop after the end of the first list:
>>> import itertools
>>> list(itertools.zip_longest(*[[k]*v for k, v in d.items()]))
[('A', 'B'), ('A', 'B'), ('A', 'B'), (None, 'B'), (None, 'B')]
Now, just flatten the list and remove None values:
>>> [v for vs in itertools.zip_longest(*[[k]*v for k, v in d.items()]) for v in vs if v is not None]
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
Other example:
>>> d = {'A': 4, 'B': 1}
>>> [v for vs in itertools.zip_longest(*[[k]*v for k, v in d.items()]) for v in vs if v is not None]
['A', 'B', 'A', 'A', 'A']
You can just use sum with a generator comprehension:
res = sum(([key]*value for key, value in d.items()), [])
This exploits the fact that sum can "add" anything that can use the + operators, like lists, in addition to sequence multiplication ("A"*4 == "AAAA").
If you want the order to be randomized, use the random module:
from random import shuffle
shuffle(res)
If, as Thierry Lathuille notes, you want to cycle through the values in the original order, you can use some itertools magic:
from itertools import chain, zip_longest
res = [*filter(
bool, # drop Nones
chain(*zip_longest(
*([key]*val for key, val in d.items()))
)
)]
As an alternative to the replication & zip_longest approach, let's try to simplify the OP's original code:
def function(dictionary):
result = []
while dictionary:
result.extend(dictionary)
dictionary = {k: v - 1 for k, v in dictionary.items() if v > 1}
return result
print(function({'A': 3, 'B': 5}))
print(function({'A': 4, 'B': 1}))
OUTPUT
% python3 test.py
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
['A', 'B', 'A', 'A', 'A']
%
Although it might look otherwise, it's not destructive on the dictionary argument, unlike the OP's original code.
It could also be done using a sort of the (position,character) tuples formed by expanding each dictionary entry:
a = {'A': 3, 'B': 5}
result = [c for _,c in sorted( (p,c) for c,n in a.items() for p,c in enumerate(c*n))]
print(result) # ['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
If the dictionary's order is usable, you can forgo the sort and use this:
result = [c for i in range(max(a.values())) for c,n in a.items() if i<n]

Converting list of lists to a dictionary with multiple values for a key

I need to write a function that accepts a list of lists representing friends for each person and need to convert it into a dictionary.
so an input of [['A','B'],['A','C'],['A','D'],['B','A'],['C','B'],['C','D'],['D','B'],['E']] should return {A:[B,C,D],B:[A],C:[B,D],D:[B],E:None}
Input:
[['A','B'],['A','C'],['A','D'],['B','A'],['C','B'],['C','D'],['D','B'],['E']]
Expected Output:
{A:[B,C,D],B:[A],C:[B,D],D:[B],E:None}
Currently I am trying the following:
s=[['A','B'],['A','C'],['A','D'],['B','A'],['C','B'],['C','D'],['D','B'],['E']]
output=dict.fromkeys((set([x[0] for x in s])),[ ])
for x in s:
if len(x)>1:
output[x[0]].append(x[1])
else:
output[x[0]].append(None)
But the output is giving me all values for every key rather than returning only the corresponding values
The output i am getting is:
{
'A': ['B', 'C', 'D', 'A', 'B', 'D', 'B', None],
'B': ['B', 'C', 'D', 'A', 'B', 'D', 'B', None],
'C': ['B', 'C', 'D', 'A', 'B', 'D', 'B', None],
'D': ['B', 'C', 'D', 'A', 'B', 'D', 'B', None],
'E': ['B', 'C', 'D', 'A', 'B', 'D', 'B', None]
}
You can iterate through the key-value pairs in the list of lists, but unpack the value as a list to accommodate the possible lack of a value:
s = [['A','B'],['A','C'],['A','D'],['B','A'],['C','B'],['C','D'],['D','B'],['E']]
output = {}
for k, *v in s:
if v:
output.setdefault(k, []).extend(v)
else:
output[k] = None
output becomes:
{'A': ['B', 'C', 'D'], 'B': ['A'], 'C': ['B', 'D'], 'D': ['B'], 'E': None}
Or if you don't mind that keys without a value get an empty list instead of None, you can simply do:
output = {}
for k, *v in s:
output.setdefault(k, []).extend(v)
output would then become:
{'A': ['B', 'C', 'D'], 'B': ['A'], 'C': ['B', 'D'], 'D': ['B'], 'E': []}
The issue is the list you feed to dict.keys is only one reference across keys.
Your desired result is inconsistent. I recommend you choose an empty list for 'E', however much it seems None is more appropriate. With this adjusted requirement, you can use collections.defaultdict.
from collections import defaultdict
L = [['A','B'],['E','C'],['A','D'],['B','A'],['C','B'],['C','D'],['D','B'],['E']]
dd = defaultdict(list)
for lst in L:
if len(lst) > 1:
dd[lst[0]].append(lst[1])
else:
dd[lst[0]]
print(dd)
defaultdict(list,
{'A': ['B', 'C', 'D'],
'B': ['A'],
'C': ['B', 'D'],
'D': ['B'],
'E': []})
You should use a common dict comprehension to initiate the dict:
output = {x[0]: [] for x in s}
dict.fromkeys gives all keys the identical referential value. With a mutable value that is a problem. The comprehension will give each key an independent list object, in addition to being more readable.
One of the way to solve this is as given below:
friend_combi = [['A','B'],['A','C'],['A','D'],['B','A'],['C','B'],['C','D'],['D','B'],['E']] # Input to be processed
final_dict = {} #Empty dict to store result
for i in friend_combi: # loop through each element in list
if final_dict.get(i[0]): #if data present in dict then append else add
final_dict[i[0]].append(i[1])
else:
final_dict[i[0]] = [i[1]] if i[1:] else None #check if value exist in list else save None
print (final_dict)
#Output --> {'A': ['B', 'C', 'D'], 'B': ['A'], 'C': ['B', 'D'], 'D': ['B'], 'E': None}
I hope this helps :)
You can define a function named get_dictionary() as the below code shows.
>>> def get_dictionary(l):
... d = {}
... for arr in l:
... if len(arr) == 2:
... key = arr[0]
... if key in d:
... d[key].append(arr[1])
... else:
... d[key] = [arr[1]]
... else:
... d[key] = None
... return d
...
>>> l = [['A','B'], ['A','C'], ['A','D'], ['B','A'], ['C','B'], ['C','D'], ['D','B'], ['E']]
>>>
>>> get_dictionary(l)
{'A': ['B', 'C', 'D'], 'B': ['A'], 'C': ['B', 'D'], 'D': None}
>>>
Pretty printing the dictionary as JSON.
>>> import json
>>>
>>> d = get_dictionary(l)
>>>
>>> print(json.dumps(d, indent=4))
{
"A": [
"B",
"C",
"D"
],
"B": [
"A"
],
"C": [
"B",
"D"
],
"D": null
}
>>>

One line iteration through dictionaries in dictionaries

G2 = {'a': {'c': 1, 'b': 1}, 'b': {'a': 1, 'c': 1}}
b = G2.values()
for i in b:
for key, value in i.items():
list.append(key)
#result: ['c', 'b', 'a', 'c']
Can I get the same result but using a list generator?
I tried it like this:
list2 = [key for key, value in i.items() for i in b]
#but i get: ['a', 'a', 'c', 'c']
just chain the dictionary values (aka keys) using itertools.chain.from_iterable, and convert to list to print the result:
import itertools
G2 = {'a': {'c': 1, 'b': 1}, 'b': {'a': 1, 'c': 1}}
#['c', 'b', 'a', 'c']
result = list(itertools.chain.from_iterable(G2.values()))
print(result)
result:
['c', 'b', 'c', 'a']
note that the order is not guaranteed as you're iterating on dictionary keys.
Variant without using itertools with flattening double loop inside comprehension (which is probably closer to your attempt):
result = [x for values in G2.values() for x in values]

How to separate a list into dictionary based on first and second values [duplicate]

This question already has answers here:
Initialize List to a variable in a Dictionary inside a loop
(2 answers)
Closed 5 years ago.
I wasn't sure exactly how to word my question, so I'll go into more depth here.
What I'm trying to do is perform the Graph Coloring problem in Python using input of a list such as this:
[('A','B'),('A','C'),('A','D'),('B','C'),('C','D')]
This is meant to denote the "neighbors" of each edge of the graph, such that A is the neighbor of B C & D, B is the neighbor of C, and C is the neighbor of D
Now, what I'm trying to do is break these into keys in a dictionary like this:
neighbors = {}
neighbors['A'] = ['B', 'C', 'D']
neighbors['B'] = ['A', 'C']
neighbors['C'] = ['A', 'B', 'D']
neighbors['D'] = ['A', 'C']
The issue I'm having is breaking down the initial input into this multi value per key dictionary. So far I have this:
neighbours = {}
myList = [('A','B'),('A','C'),('A','D'),('B','C'),('C','D')]
for i in myList:
neighbours[i[0]] = (i[1])
print(neighbours)
This provides the output:
{'A': 'D', 'C': 'D', 'B': 'C'}
But I'd like to have it look like this:
{'A': ['B','C','D'], 'B': ['A','C'], 'C': ['A','B','D'], 'D': ['A','C']}
Any help is greatly appreciated! Thanks :)
The straight forward, EAFP approach:
adj = [('A','B'),('A','C'),('A','D'),('B','C'),('C','D')]
mat = {}
for (x, y) in adj:
try:
mat[x].append(y)
except KeyError:
mat[x] = [y]
try:
mat[y].append(x)
except KeyError:
mat[y] = [x]
>>> mat
{'A': ['B', 'C', 'D'], 'C': ['A', 'B', 'D'], 'B': ['A', 'C'], 'D': ['A', 'C']}
Or, if you prefer, the default dict:
from collections import defaultdict
default = defaultdict(list)
for (x, y) in adj:
default[x].append(y)
default[y].append(x)
>>> default
defaultdict(<type 'list'>, {'A': ['B', 'C', 'D'], 'C': ['A', 'B', 'D'], 'B': ['A', 'C'], 'D': ['A', 'C']})
Which is 10-20% faster, if you're interested in performance. (see this repl.it comparison)
>>> l = [('A','B'),('A','C'),('A','D'),('B','C'),('C','D')]
>>>
>>> d = {}
>>> for i in l:
... temp = d.get(i[0], [])
... temp.append(i[1])
... d[i[0]] = temp
Why not create a list, and add every element to it?
neighbours = {}
myList = [('A','B'),('A','C'),('A','D'),('B','C'),('C','D')]
for i in myList:
if i[0] not in neighbours:
neighbours[i[0]]= list()
neighbours[i[0]].append(i[1])
print(neighbours)
Edit: resuls in:
{'B': ['C'], 'A': ['B', 'C', 'D'], 'C': ['D']}

Python group two lists

I have two lists:
A = ['T', 'D', 'Q', 'D', 'D']
sessionid = [1, 1, 1, 2, 2]
Is there anyway i could group items in A for the same sessionid, so that i could print out the following:
1: ["T", "D","Q"]
2: ["D","D"]
The itertools groupby function is designed to do this sort of thing. Some of the other answers here create a dictionary, which is very sensible, but if you don't actually want a dict then you can do this:
from itertools import groupby
from operator import itemgetter
A = ['T', 'D', 'Q', 'D', 'D']
sessionid = [1, 1, 1, 2, 2]
for k, g in groupby(zip(sessionid, A), itemgetter(0)):
print('{}: {}'.format(k, list(list(zip(*g))[1])))
output
1: ['T', 'D', 'Q']
2: ['D', 'D']
operator.itemgetter(0) returns a callable that fetches the item at index 0 of whatever object you pass it; groupby uses this as the key function to determine what items can be grouped together.
Note that this and similar solutions assume that the sessionid indices are sorted. If they aren't then you need to sort the list of tuples returned by zip(sessionid, A) with the same key function before passing them to groupby.
edited to work correctly on Python 2 and Python 3
Not using itertools, you can use a dictionary:
index = 0
dict = {}
for i in sessionid:
if not (i in dict):
dict[i] = []
else:
dict[i].append(A[index])
index += 1
print(dict) # {1: ['T', 'D', 'Q'], 2: ['D', 'D']}
And based on the remarks below:
from collections import defaultdict
dict = defaultdict(list)
for i, item in enumerate(sessionid):
dict[item].append(A[i])
You could use a dictionary and zip:
A = ['T', 'D', 'Q', 'D', 'D']
sessionid = [1, 1, 1, 2, 2]
result = {i:[] for i in sessionid}
for i,j in zip(sessionid,A):
result[i].append(j)
Or you can use defaultdict:
from collections import defaultdict
result = defaultdict(list)
for k, v in zip(sessionid, A):
result[k].append(v)
Output:
>>> result
{1: ['T', 'D', 'Q'], 2: ['D', 'D']}
One liner
{k: list(i for (i, _) in v) for k, v in itertools.groupby(zip(A, sessionid), operator.itemgetter(1))}
Without nested loop
{k: list(map(operator.itemgetter(0), v)) for k, v in itertools.groupby(zip(A, sessionid), operator.itemgetter(1))}
You can do:
import pandas as pd
A = ['T', 'D', 'Q', 'D', 'D']
sessionid = [1, 1, 1, 2, 2]
pd.DataFrame({'A':A, 'id':sessionid}).groupby('id')['A'].apply(list).to_dict()
#Out[10]: {1: ['T', 'D', 'Q'], 2: ['D', 'D']}
You could also convert them into numpy arrays, and use the indices of the session ids you need with np.where
import numpy as np
A = np.asarray(['T', 'D', 'Q', 'D', 'D'])
sessionid = np.asarray([1, 1, 1, 2, 2])
Ind_1 = np.where(sessionid == 1)
Ind_2 = np.where(sessionid == 2)
print A[Ind_1]
should return ['T' 'D' 'Q']
you could of course turn this into a function which takes N, the desired session and returns your A values.
Hope this helps!

Categories