make from a list the dictionary lists by key - python

I have a list:
['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS',
'QS', 'TS', 'JS', 'KS']
I need to get a dictionary something like this: (sorting is not important)
{'C': ['QC', '8C', '7C', '5C', '3C', '2C'],
'S': ['AS', 'KS', 'QS', 'JS', 'TS']
}
code:
def parse_flush(cards):
cards = sort_by_color(cards)
flush_dic = {}
print str(cards)
count = 0
pos = 0
last_index = 0
for color in colors:
for i, card in enumerate(cards):
if card[1] == color:
count += 1
last_index = i+1
if count == 1:
pos = i
if count >= 5:
flush_dic[color] = sort_high_to_low(cards[pos:last_index])
count = 0
return flush_dic
my code now looks like, it works but I do not like its length it is possible to make it shorter using python tricks?

You can use simple collections.defaultdict to get the results you wanted
from collections import defaultdict
result = defaultdict(list)
for item in data:
result[item[1]].append(item)
print result
Output
{'S': ['AS', 'QS', 'TS', 'JS', 'KS'],
'H': ['TH'],
'C': ['8C', '2C', 'QC', '5C', '7C', '3C'],
'D': ['6D', 'TD']}
You can solve this, using itertools.groupby as well
data = ['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS', 'QS',
'TS', 'JS', 'KS']
from itertools import groupby
from operator import itemgetter
keyFn = itemgetter(1)
print {k:list(grp) for k, grp in groupby(sorted(data, key = keyFn), keyFn)}
Explanation
sorted returns a sorted list of items, and it uses keyFn for sorting the data.
groupby accepts a sorted list and it groups the items based on the keyFn, in this case keyFn returns the second elements for each and every items and the result is as seen in the output.

Use a very simple for loop:
>>> l = ['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS',
... 'QS', 'TS', 'JS', 'KS']
>>> my_dict = {}
>>> for x in l:
... my_dict.setdefault(x[-1],[]).append(x)
...
>>> my_dict
{'S': ['AS', 'QS', 'TS', 'JS', 'KS'], 'H': ['TH'], 'C': ['8C', '2C', 'QC', '5C', '7C', '3C'], 'D': ['6D', 'TD']}

data = ['8C', '2C', 'QC', '5C', '7C', '3C', '6D', 'TD', 'TH', 'AS', 'QS',
'TS', 'JS', 'KS']
dic = {}
for i in data:
try:
dic[i[1]].append(i)
except:
dic[i[1]] = []
dic[i[1]].append(i)
print dic
Output
{'S': ['AS', 'QS', 'TS', 'JS', 'KS'],
'H': ['TH'],
'C': ['8C', '2C', 'QC', '5C', '7C', '3C'],
'D': ['6D', 'TD']}

Related

how to reduce memory usage in kaggle for python code

import itertools
deck = ['AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '10D', 'JD', 'QD', 'KD',
'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '10C', 'JC', 'QC', 'KC',
'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '10H', 'JH', 'QH', 'KH',
'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '10S', 'JS', 'QS', 'KS']
combinations = list(itertools.combinations(deck, 9))
i try to find all this combinations then i will load this combinations in a csv file but kaggle gives me this error message:
Your notebook tried to allocate more memory than is available. It has restarted.
Don't use a list, just write line after line.
itertools.combinations create an iterator that allows you to iterate over each value without having to create a list and store each value in memory.
You can use the csv module to write each combination as a line.
If you don't want an empty line between each combination, don't forget to use the newline='' in open: https://stackoverflow.com/a/3348664/6251742.
import csv
import itertools
deck = ['AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '10D', 'JD', 'QD', 'KD',
'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '10C', 'JC', 'QC', 'KC',
'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '10H', 'JH', 'QH', 'KH',
'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '10S', 'JS', 'QS', 'KS']
combinations = itertools.combinations(deck, 9)
with open('combinations.csv', 'w', newline='') as file:
writer = csv.writer(file, delimiter=',')
for combination in combinations:
writer.writerow(combination)
Result after some time:
AD,2D,3D,4D,5D,6D,7D,8D,9D
AD,2D,3D,4D,5D,6D,7D,8D,10D
AD,2D,3D,4D,5D,6D,7D,8D,JD
AD,2D,3D,4D,5D,6D,7D,8D,QD
AD,2D,3D,4D,5D,6D,7D,8D,KD
... # 3679075395 more lines, 98.3 GB

Countring occurences of strings in a list even if the string is reversed

I have a list of letter-pairs whose occurences I need to count in a next step but their reverses are considered the same thing, so 'mo' and 'om' would be 2 occurences of 'mo' and so on -
i figured I would transform the list of strings into a list where I reversed the strings and then counted their occurences so this list so I can have a dictionary with how often they occur:
pairs = ['mo', 'om', 'mo', 'om', 'ab', 'ba', 'ab', 'ba', 'ab', 'af', 'fa', 'fa', 'fa']
counted_pairs = [pairs.count(i) for i in pairs]
my_dictionary = dict(zip(pairs,counted_pairs))
Should be transformed into this:
pairs = ['mo', 'mo', 'mo', 'mo', 'ab', 'ab', 'ab', 'ab', 'ab', 'af', 'af', 'af', 'af']
counted_pairs = [pairs.count(i) for i in pairs]
my_dictionary = dict(zip(pairs,counted_pairs))
in order for me to count them.
I came up with this code:
pairs = ['mo', 'om', 'mo', 'om', 'ab', 'ba', 'ab', 'ba', 'ab', 'af', 'fa', 'fa', 'fa']
loop_pairs = []
for i in range(0,len(pairs)):
word = pairs[i]
for el in range(0,len(pairs)):
if word == pairs[el]:
loop_pairs.append(pairs[el])
else:
if word == pairs[el][::-1]:
loop_pairs.append(pairs[el][::-1])
and it works in so far that it appends the reversed pairs to the new list, but I would then need to delete the rest of the occurences because it goes on to then examine the next object and since I already counted that in the first round of the outer loop this is not what I need anymore.
I hope this is clear, thank you!
Just sort each pair into a consistent order before counting:
>>> from collections import Counter
>>> pairs = ['mo', 'om', 'mo', 'om', 'ab', 'ba', 'ab', 'ba', 'ab', 'af', 'fa', 'fa', 'fa']
>>> counts = Counter("".join(sorted(p)) for p in pairs)
>>> counts
Counter({'ab': 5, 'mo': 4, 'af': 4})

Removing and Converting Values of a dictionary

I have a dictionary with multiple values:
wd = {'A1': ('01', '10', '0001', '0010', '0100', '1000'), 'A2':('02', '20', '0002', '0020', '0200', '2000')
What I'm attempting to do is make is so if the string in the values begins with '1' or '2' that my dictionary will remove it from the dictionary so '10', '1000', '20', '2000' will be removed. Here's the code I'm trying:
remove = ('1'), ('2')
for k,v in wd.items():
for c in k:
if c.startswith(remove):
wd = {x for x in wd if x not in remove}
print(wd)
Expected Output:
wd = {'A1': ('01', '0001', '0010', '0100'), 'A2':('02', '0002', '0020', '0200')
I'm also trying to make it so if I have a string:
mystr = '0010'
That my dictionary will be able to convert that string to the key in my dictionary so '0010' will become 'A1' since the value of '0010' is paired to key 'A1'. I'm able to successfully do this with a dictionary with single key,value pairs but when it comes to multiple values I get stumped.
This is the code I'm working with:
for k,v in wd.items():
enstr = ''.join(str(wd.get(c)) for c in mystr)
print(enstr)
Output:
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
NoneNoneNoneNone
Expected Output when I print(enstr):
'A1'
If I get this right, you want to remove values which start with anything given by remove and further you want to group the dictionary by values.. so you want a map from a value to it's group e.g. 01 -> A1.
The following should do this:
from itertools import cycle
wd = {'A1': ('01', '10', '0001', '0010', '0100', '1000'), 'A2':('02', '20', '0002', '0020', '0200', '2000')}
remove = ('1'), ('2')
for k, values in wd.items():
wd[k] = [
value for value in values
if not any([value.startswith(r) for r in remove])
]
lists = list()
for k, v in wd.items():
lists += list(map(lambda x: x[::-1], zip(cycle([k]), v)))
print(wd)
print(dict(lists))
which gives:
{'A1': ['01', '0001', '0010', '0100'], 'A2': ['02', '0002', '0020', '0200']}
{'0001': 'A1', '0200': 'A2', '0020': 'A2', '0100': 'A1', '02': 'A2', '0010': 'A1', '0002': 'A2', '01': 'A1'}
wd = {
key: list(filter(lambda x: x[0] not in ['1', '2'], val))
for key, val in wd.items()
}
For the second part of your problem:
mystr = '0010'
wd = {'A1': ('01', '10', '0001', '0010', '0100', '1000'), 'A2':('02', '20', '0002', '0020', '0200', '2000')
for k,v in wd.items():
if mystr in v:
print(k)

How to combine two lists into pairs and then make those pairs one element in list?

first=[1,2,3,4,5]
second=['a','b','c','d','e']
final=['1a','2a','3a','1b','2b',3b','1c','2c','3c']
I want to combine two lists in python but I don't care about order. Aka I don't want '1a' and 'a1'.
>>> import itertools
>>> first=[1,2,3,4,5]
>>> second=['a','b','c','d','e']
>>> final = [''.join(str(i) for i in s) for s in itertools.product(first, second)]
>>> final
['1a', '1b', '1c', '1d', '1e', '2a', '2b', '2c', '2d', '2e', '3a', '3b', '3c', '3d', '3e', '4a', '4b', '4c', '4d', '4e', '5a', '5b', '5c', '5d', '5e']
A simple list comprehension will work.
print([str(first[i])+second[i] for i in range(len(first))])
final = list()
for i in first:
for j in second:
final.append(str(i)+j)
If you've only got two sequences to "multiply" like this, and your iteration is dead-simple, a nested loop in a comprehension is perfectly readable:
['{}{}'.format(a, b) for a in first for b in second]
If you have a longer, or dynamic, list, you want itertools.product, as in inspectorG4dget's answer.
If you have anything more complicated than just iterating over the product, you probably want explicit loop statements rather than a comprehension (or maybe factor part of it out into a generator function and use that with the nested comp or product call).
One way without using itertools, map or zip is:
first = [1, 2, 3, 4, 5]
second = ['a', 'b', 'c', 'd', 'e']
print [str(i) + j for i in first for j in second]
Output:
['1a', '1b', '1c', '1d', '1e', '2a', '2b', '2c', '2d', '2e', '3a', '3b', '3c', '3d', '3e', '4a', '4b', '4c', '4d', '4e', '5a', '5b', '5c', '5d', '5e']

How to get every element in a list of list of lists?

I'm making a heart game for my assignment but I don't know how to get every element in a list of list:
>>>Cards = [[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C],["JH"]],[["7D"]]]
and what comes to my mind is :
for values in cards:
for value in values:
But I think I just got element that has 2 list. How to calculate the one that has 3 and 1 list in the cards?
Like this:
>>> Cards = [[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],["7D"]]
>>> from compiler.ast import flatten
>>> flatten(Cards)
['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7D']
As, nacholibre pointed out, the compiler package is deprecated. This is the source of flatten:
def flatten(seq):
l = []
for elt in seq:
t = type(elt)
if t is tuple or t is list:
for elt2 in flatten(elt):
l.append(elt2)
else:
l.append(elt)
return l
Slightly obscure oneliner:
>>> [a for c in Cards for b in c for a in b]
['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7', 'D']
You might want to give a, b and c more descriptive names.
If your cards are nested in a unwieldy way:
>>> Cards = [[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],["7D"]]
>>> def getCards(cardList,myCards=[]): #change this to myCards, and pass in a list to mutate this is just for demo
if isinstance(cardList,list):
for subList in cardList:
getCards(subList)
else:
myCards.append(cardList)
return myCards
>>> getCards(Cards)
['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7D']
Will recursivly go through the list and find all the elements. These are some timeings I've run comparing performance of the selected flattern method to mine:
>>> print(timeit.timeit(r'getCards([[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],["7D"]],[])',setup="from clas import getCards"))
5.24880099297
>>> timeit.timeit(r'flatten([[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],["7D"]])',setup="from compiler.ast import flatten")
7.010887145996094
Your list is an incomplete nested list, so you can first make it rectangular, using the procedure explained here, for example, and then flatten the resulting numpy.ndarray.
The "ifs" below wouldn't be necessary as well if the last element ['7D'] was [['7D']] (then the other answers would also work).
import numpy as np
collector = np.zeros((3,3,3),dtype='|S20')
for (i,j,k), v in np.ndenumerate( collector ):
try:
if not isinstance(cards[i], str):
if not isinstance(cards[i][j], str):
collector[i,j,k] = cards[i][j][k]
else:
collector[i,j,0] = cards[i][j]
else:
collector[i,0,0] = cards[i]
except IndexError:
collector[i,j,k] = ''
print collector[collector<>''].flatten()
Using generators, it's possible to write a much more readable implementation of flatten:
def flatten(l):
if isinstance(l, list):
for e1 in l:
for e2 in flatten(e1):
yield e2
else:
yield l
Or, if you're using Python 3.3, which added the yield from syntax:
def flatten(l):
if isinstance(l, list):
for e in l:
yield from flatten(e)
else:
yield l
Result:
>>> list(flatten([[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],[["7D"]]]))
['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7D']
Use 2 nested itertools.chain to flatten the list:
In [32]: Cards
Out[32]: [[['QS', '5H', 'AS'], ['2H', '8H'], ['7C']], [['9H', '5C'], ['JH']], ['7D']]
In [33]: from itertools import chain
In [34]: [k for k in chain.from_iterable([i for i in chain.from_iterable(Cards)])]
Out[34]: ['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7', 'D']
This solution is very robust for any kind of nested lists or tuples (to add other iterable types just add more or isinstance(...) in the code below.
It just calls recursively a function that unfolds itself:
def unfold(lst):
output = []
def _unfold(i):
if isinstance(i, list) or isinstance(i, tuple):
[_unfold(j) for j in i]
else:
output.append(i)
_unfold(lst)
return output
print unfold(cards)
#['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7D']
Using Flatten a list from Rosetta Code you could do:
>>> def flatten(lst):
return sum( ([x] if not isinstance(x, list) else flatten(x)
for x in lst), [] )
>>> Cards = [[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],["7D"]]
>>> flatten(Cards)
['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7D']
>>>
The solution only flattens nested lists - not tuples or strings.
from itertools import chain, imap
l= [[["QS","5H","AS"],["2H","8H"],["7C"]],[["9H","5C"],["JH"]],[["7D"]]]
k = list(chain.from_iterable(imap(list, l)))
m = list(chain.from_iterable(imap(list, k)))
print m
output: ['QS', '5H', 'AS', '2H', '8H', '7C', '9H', '5C', 'JH', '7D']
Itertools is amazing!

Categories

Resources