Reiterating over lists and dictionaries - python

In the following code, why does my code not iterate properly? I'm probably missing one line but I can't figure out why it doesn't work.
I have a function with the following test case:
>>> borda([['A', 'B', 'C', 'D'], ['B', 'A', 'C', 'D'], ['B', 'C', 'D', 'A']])
('B', [5, 8, 4, 1])
Where lists in the parameter are rankings, each #1 rank gets 3 points, #2 gets 2 points, #3 gets 1 point, and no other ranks get anything. There may not necessarily four choices. The first element in the tuple should be the choice with the highest number of points, and the second element is the number of points each choice got, in alphabetical order.
I'm not done with the function, but I'm trying to get a dictionary of the choices as the keys in alphabetical order and the count of rankings as the values, but the output is a dictionary of only the very last element of the last list in the parameter.
L = ['A', 'B', 'C', 'D'] #This is referenced outside the function since it might change
D = {}
i = 0
num = 0
while num < len(L):
num += 1
for choice in L:
while i < len(parameter):
for item in parameter:
if item[0] == choice:
D[choice] = D.get(choice, 0) + 3
if item[1] == choice:
D[choice] = D.get(choice, 0) + 2
if item[2] == choice:
D[choice] = D.get(choice, 0) + 1
i += 1
return D

The way I'd do this is something like this:
import operator
from collections import defaultdict
listoflists = [['A', 'B', 'C', 'D'], ['B', 'A', 'C', 'D'], ['B', 'C', 'D', 'A']]
def borda(listoflists):
outdict = defaultdict(int)
for item in listoflists:
outdict[item[0]] += 3
outdict[item[1]] += 2
outdict[item[2]] += 1
highestitem = max(outdict.iteritems(), key=operator.itemgetter(1))[0]
outlist = [outdict[item[0]] for item in sorted(outdict.keys())]
return (highestitem, outlist)
Update:
I'm not sure why you wouldn't be able to import standard modules, but if for whatever reason you're forbidden from using the import statement, here's a version with only built-in functions:
listoflists = [['A', 'B', 'C', 'D'], ['B', 'A', 'C', 'D'], ['B', 'C', 'D', 'A']]
def borda(listoflists):
outdict = {}
for singlelist in listoflists:
# Below, we're just turning singlelist around in order to
# make use of index numbers from enumerate to add to the scores
for index, item in enumerate(singlelist[2::-1]):
if item not in outdict:
outdict[item] = index + 1
else:
outdict[item] += index + 1
highestitem = max(outdict.iteritems(), key=lambda i: i[1])[0]
outlist = [outdict[item[0]] for item in sorted(outdict.keys())]
return (highestitem, outlist)

If you had 2.7:
import operator
from collections import Counter
listoflists = [['A', 'B', 'C', 'D'], ['B', 'A', 'C', 'D'], ['B', 'C', 'D', 'A']]
def borda(listoflists):
outdict = sum([Counter({item[x]:3-x}) for item in listoflists for x in range(3]],
Counter())
highestitem = max(outdict.iteritems(), key=operator.itemgetter(1))[0]
outlist = [outdict[item[0]] for item in sorted(outdict.iteritems(),
key=operator.itemgetter(0))]
return (highestitem, outlist)
Look ma.. no loops :-)
Check out http://ua.pycon.org/static/talks/kachayev/index.html to see why this is better.

Related

Check how many items from one list appear in another list

If I have 2 lists, for an example:
list_1 = ['a', 'A']
list_2 = ['a', 'A', 'A', 'b', 'b', 'b', 'b']
How can I make a count to see how many times the items from list_1 appear in list_2?
So in this case it should return 3.
list_1 = ['a', 'A']
list_2 = ['a', 'A', 'A', 'b', 'b', 'b', 'b']
found = 0
for x in list_1:
for y in list_2:
if x == y:
found += 1
print(found)
An efficient O(n) method using a set as reference:
list_1 = ['a', 'A']
list_2 = ['a', 'A', 'A', 'b', 'b', 'b', 'b']
set_1 = set(list_1)
count = 0
for e in list_2:
if e in set_1:
counter += 1
Output: 3
A one liner:
sum([x == y for x in list_1 for y in list_2])
Another solution, which could be beneficial in other cases, because the Counter()- object returns a dictionary with the already summed up elements
from collections import Counter
list_1 = ['a', 'A']
list_2 = ['a', 'A', 'A', 'b', 'b', 'b', 'b']
c = Counter(list_2)
print(sum([c[x] for x in list_1]))
Just use count for lists:
print(list_2.count(list_1[0]) + list_2.count(list_1[1]))
or in a loop:
sum_ = 0
for i in range(len(list_1)):
sum_ += list_2.count(list_1[i])
print(sum_)

Why my code not working for python list rotate

My Code. I have wrote the code for rotating the list
s = 'abc'
lst = list(s)
for x in range(0,len(lst)):
lst = lst[-x:] + lst[:-x]
print (lst)
My Out
['a', 'b', 'c']
['c', 'a', 'b']
['a', 'b', 'c']
Expected Out
['a', 'b', 'c']
['c', 'a', 'b']
['b', 'c', 'a']
Because you're overwritting you're original list and hence in the second iteration you're rotating and additional position and ending up with the same list. Create a temporary variable:
s = 'abc'
lst = list(s)
for x in range(0,len(lst)):
lst_ = lst[-x:] + lst[:-x]
print (lst_)
['a', 'b', 'c']
['c', 'a', 'b']
['b', 'c', 'a']
Just as a side note - you might find collections.deque interesting for such task:
from collections import deque
d = deque(s)
for _ in range(len(s)):
print(d)
d.rotate()
deque(['a', 'b', 'c'])
deque(['c', 'a', 'b'])
deque(['b', 'c', 'a'])
Alternatively to the answer of #yatu, you can rotate your list by only one element in each loop iteration:
s = 'abc'
lst = list(s)
print(lst)
for x in range(0,len(lst)-1):
lst = lst[-1:] + lst[:-1]
print (lst)
use deque from collections
from collections import deque
foo = ['a', 'b', 'c']
bar = deque(foo)
for _ in range(len(bar)):
print(bar) # or ptint(list(bar))
bar.rotate()
You can rotate in both directions and at arbitrary steps.

Deleting elements of a list based on a condition

I have a list of elements from which I want to remove those elements whose count is less than or equal to 2 in all the list.
For example:
A = [['a','b','c'],['b','d'],['c','d','e'],['c','e','f'],['b','c','e','g']]
I want to remove 'a', 'd', 'f', 'g' from A and store the rest in B so that the list becomes:
B = [['b','c'],['b'],['c','e'],['c','e'],['b','c','e']]
I created a dictionary which will store all the count of elements and based on that I want to remove the elements with count less than or equal to 2.
Below is the code which I have written so far.
for i in range(len(A)):
for words in A[i]:
word_count[words] +=1
B = [A[i] for i in range(len(A)) if word_count[words]<2]
You can use collections.Counter:
from collections import Counter
import itertools
A = [['a','b','c'],['b','d'],['c','d','e'],['c','e','f'],['b','c','e','g']]
c = Counter(itertools.chain(*A))
new_a = [[b for b in i if c[b] > 2] for i in A]
Output:
[['b', 'c'], ['b'], ['c', 'e'], ['c', 'e'], ['b', 'c', 'e']]
Before you add a new key to the dictionary, you have to check if the key exists. If not, just add the key to the dictionary. Otherwise, update the key's value.
A = [['a','b','c'],['b','d'],['c','d','e'],['c','e','f'],['b','c','e','g']]
word_count = {}
for i in range(len(A)):
for words in A[i]:
if words not in word_count:
word_count[words] = 0
word_count[words] += 1
Then filter the initial list using the created dictionary.
B = [[x for x in A[i] if word_count[x] > 2] for i in range(len(A))]
print(B)
Output
[['b', 'c'], ['b'], ['c', 'e'], ['c', 'e'], ['b', 'c', 'e']]

Python, work with list, find max sequence length

for example test_list:
test_list = ['a', 'a', 'a', 'b', 'b', 'a', 'c', 'b', 'a', 'a']
what tool or algorithm i need to use, to get max sequences count, for this example:
'a' = 3
'b' = 2
'c = 1
Using a dict to track max lengths, and itertools.groupby to group the sequences by consecutive value:
from itertools import groupby
max_count = {}
for val, grp in groupby(test_list):
count = sum(1 for _ in grp)
if count > max_count.get(val, 0):
max_count[val] = count
Demo:
>>> from itertools import groupby
>>> test_list = ['a', 'a', 'a', 'b', 'b', 'a', 'c', 'b', 'a', 'a']
>>> max_count = {}
>>> for val, grp in groupby(test_list):
... count = sum(1 for _ in grp)
... if count > max_count.get(val, 0):
... max_count[val] = count
...
>>> max_count
{'a': 3, 'c': 1, 'b': 2}
Here is a direct way to do it:
Counts, Count, Last_item = {}, 0, None
test_list = ['a', 'a', 'a', 'b', 'b', 'a', 'c', 'b', 'a', 'a']
for item in test_list:
if Last_item == item:
Count+=1
else:
Count=1
Last_item=item
if Count>Counts.get(item, 0):
Counts[item]=Count
print Counts
# {'a': 3, 'c': 1, 'b': 2}
You should read about what a dictionary is (dict in Python) and how you could store how many occurrences there are for a sequence.
Then figure out how to code the logic -
Figure out how to loop over your list. As you go, for every item -
If it isn't the same as the previous item
Store how many times you saw the previous item in a row into the dictionary
Else
Increment how many times you've seen the item in the current sequence
Print your results
You can use re module for find all sequences of the character in a string composed by all the characters in your list. Then just pick the largest string for a single character.
import re
test_list = ['a', 'a', 'b', 'b', 'a', 'c', 'b', 'a', 'a', 'a']
# First obtain the characters.
unique = set(test_list)
max_count = {}
for elem in unique:
# Find all sequences for the same character.
result = re.findall('{0}+'.format(elem), "".join(test_list))
# Find the longest.
maximun = max(result)
# Save result.
max_count.update({elem: len(maximun)})
print(max_count)
This will print: {'c': 1, 'b': 2, 'a': 3}
For Python, Martijn Pieters' groupby is the best answer.
That said, here is a 'basic' way to do it that could be translated to any language:
test_list = ['a', 'a', 'a', 'b', 'b', 'a', 'c', 'b', 'a', 'a']
hm={}.fromkeys(set(test_list), 0)
idx=0
ll=len(test_list)
while idx<ll:
item=test_list[idx]
start=idx
while idx<ll and test_list[idx]==item:
idx+=1
end=idx
hm[item]=max(hm[item],end-start)
print hm
# {'a': 3, 'c': 1, 'b': 2}

How do you remove a sublist if a certain element is not found at a specific position across all sublists?

In other words, if it's found that "f" is in the 4th position of the sublist, return that sublist, otherwise, exclude it if "f" is not found.
List = [['a','b','c','d','f'],['a','b','c','d','e'],['a','b','c','d','e'],['a','b','c','f','f'],['a','b']]
I have the following function which would work if all the sublists were the same size.
def Function(SM):
return filter(lambda x: re.search("f",str(x[4])),List)
IndexError: list index out of range
Desired_List = [['a','b','c','d','f'],['a','b','c','f','f']]
I'm reluctant to use a for loop, because of the speed and efficiency costs. Are there any alternatives that are just as quick?
You can use list comprehension:
lst = [['a','b','c'], ['a','b','c','d','f'],['a','b','c','d','e'],['a','b','c','d','e'],['a','b','c','f','f'],['a','b']]
lst_desired = [l for l in lst if len(l) >= 5 and l[4] == "f"]
print lst_desired
Output
[['a', 'b', 'c', 'd', 'f'], ['a', 'b', 'c', 'f', 'f']]
>>> li=[['a','b','c','d','f'],['a','b','c','d','e'],['a','b','c','d','e'],['a','b','c','f','f'],['a','b']]
>>> filter(lambda l: l[4:5]==['f'], li)
[['a', 'b', 'c', 'd', 'f'], ['a', 'b', 'c', 'f', 'f']]

Categories