Related
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]
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
}
>>>
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]
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']}
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!