Creating a dictionary from list of lists using only one for loop - python-3.x

I came across this question in a test. There are two parts to this question:
Part i:
Given a list of flavors, eg. ['A','A','A','A','B','B','B','B','B','C','C','C','C'], write a function that returns a dictionary of the number of each flavor respectively.
My solution:
flavors = ['A','A','A','A','B','B','B','B','B','C','C','C','C']
def count_flavors(l):
dict_flavors={}
for i in l:
dict_flavors[i] = l.count(i)
return dict_flavors
print(count_flavors(flavors))
Part ii:
Using not more than ONE for loop write a function that accepts a list of lists of flavors eg. [['A', 'A', 'B', 'B', 'B', 'C', 'C'], ['A', 'A', 'B', 'B', 'B', 'B', 'C'], ['A', 'B', 'C', 'C']] and returns a dictionary for the total number of each flavor. You must include the function that you defined in part one in this solution.
(To clarify, essentially there should only be two for loops; one from part one and one from part two)
So far my solution is the following:
batches = [['A','A','A','A','B','B','B','B','B','C','C','C','C'], ['A', 'A', 'B', 'B' ,'B','B','C'], ['A','B','C','C']]
def batch_count(b):
batch_dict = []
result = {}
for j in b:
batch_dict.append(count_flavors(j))
print(batch_dict)
for i in batch_dict:
for k in i.keys():
result[k] = result.get(k,0) + i[k]
return result
print('batch count 1:' + str(batch_count(batches)))
I am struggling to find a solution that only uses one for loop for this part. I am aware that there are modules that exist for this sort of thing like collections.Counter(). Is a naive solution that does not include any modules possible for this problem?
Thanks!

Here is the best naive solution which I can think of in order to achieve what you want
Benefits of using the solution
No need to create extra variable like batch_dict = [], which takes unnecessary space in your system
No need to carry out multiple computations using different methods, like you did above using count_flavors()
Straight forward and easy to understand
FINAL SOLUTION
batches = [['A','A','A','A','B','B','B','B','B','C','C','C','C'], ['A', 'A', 'B', 'B' ,'B','B','C'], ['A','B','C','C']]
def batch_count(b):
result = {} # for storing final count results
# two loops are required to get into the arrays of array, not other option is there
for items in b:
# Getting the nested array item here
for item in items:
# final computation, if the item is there in the result dict, then increment
# else simply assign 1 to the item as a key which eventually gives you the total number
# of counts of each item throughout the batches array items
if item in result:
result[item] += 1
else:
result[item] = 1
return result
print('batch count 1:' + str(batch_count(batches)))
# OUTPUT
# >>> batch count 1:{'A': 7, 'C': 7, 'B': 10}
Feel free to test this out for some other batches too, and let me know. This is by far the naive solution which is possible to give out what you want to achieve. Keep learning :)
ANOTHER SOLUTION [MAKING USE OF FIRST METHOD COUNT_FLAVORS]
Hey, if you really want to use the first method, then there is a work around, but you need to compromise with one thing now, that is Counter has to be imported, but I assure you, it will be as simple as that, and will give you straight forward answer
Your count_flavors works fine, so we take the count_falvors() as is.
We will be making changes to the batch_count method now
FINAL SOLUTION
from collections import Counter
# Taking your method as is, to get the dictionary which counts
# the items occurence from your array
def count_flavors(l):
dict_flavors={}
for i in l:
dict_flavors[i] = l.count(i)
return dict_flavors
# This method will do your stuffs
def batch_count(b):
result = {} #this will be used to return the final result
# now just one loop, since we will passing the array
# to our method for computation count_flavors()
for items in b: # this will give out single array
'''
now we will call your count_flavor method
we will use Counter() to merge the dictionary data
coming from the count_flavor and then add it to the result
Counter() keep track of same item, if present in multiple
dict, ADDS +1 to the same item, doesn't duplicate value
Hence counter required
'''
if len(result) != 0:
# if the result is not empty, then result = result + data
result += Counter(count_flavors(items)) # no more extra for loop
else:
# else first fill the data by assigning it
result = Counter(count_flavors(items))
# this will give out the output in {}
# else the output will come in Counter({}) format
return dict(result)
# our test array of arrays
batches = [['A','A','A','A','B','B','B','B','B','C','C','C','C'], ['A', 'A', 'B', 'B' ,'B','B','C'], ['A','B','C','C']]
print('batch count 1:' + str(batch_count(batches)))
# OUTPUT
# >>> batch count 1:{'A': 7, 'B': 10, 'C': 7}
In this way you achieve the output with the usage of your count_flavors() method too, that too with no multiple loops in the batch_count(). Hope that will give you more clarity :). If this works out for you, you may accept the answer, for the the people who will come looking for answer to this question :)

The first function can become much faster by modifying your approach in this way:
def count_flavors(lst):
dict_flavors = {}
for item in lst:
if item in dict_flavors:
dict_flavors[item] += 1
else:
dict_flavors[item] = 1
return dict_flavors
You could also use Counter to simplify your code:
from collections import Counter
def count_flavors(lst):
return dict(Counter(lst))
The second function can use itertools.chain:
from collections import Counter
from itertools import chain
def batch_count(b):
return dict(Counter(chain(*b)))

Related

How to create a chaining or cyclic sequence with a list in Python, so that after the last index of a list we can start from first index again

I have an alphabets list:
alpha_list = ['a', 'b', 'c', 'd', 'e']
For a given alphabet(considering it will always be present in alpha_list) I want to get an alphabet whose index is garter by a given number, consider below function for example:
def get_replacing_letter(alphabet, number):
index = alpha_list.index(alphabet)
return alpha_list[index + number]
get_replacing_letter('a', 2) will give me 'c'
what I want is get_replacing_letter('d', 2) should give 'a'
similarly get_replacing_letter('e', 2) should give 'b'
So the alph_list should work in a chaining sequence or cyclic manner. I am wondering how to achieve this in Python?
You can make new index take the modulo of the length of the list:
return alpha_list[(index + number) % len(alpha_list)]
At the moment I achieved it by extending the alpha_list with itself:
alpha_list.extend(alpha_list)
This way as index method gives first index of occurance, I could manage to get it working.
However I am wondering if there is any better way to achieve this.

Is there a simpler way to extract the last value of a dictionary?

So I was tasked to make a function using python, that returns how many values there is in a dictionary that ONLY contains lists. An example of such a dictionary would be:
animals = { 'a': ['alpaca','ardvark'], 'b': ['baboon'], 'c': ['coati']}
The values inside the list also count towards the total values returned from the function, which means that it has to return 4. This is the function I made:
def how_many(aDict):
'''
aDict: A dictionary, where all the values are lists.
returns: int, how many values are in the dictionary.
'''
numValues = 0;
while aDict != {}:
tupKeyValue = aDict.popitem();
List = tupKeyValue[1];
numValues += len(List);
return numValues;
So I was wondering if there was a way to pop the last value of a dictionary without popitem() which extracts the key-value pair. Just trying to make it as simple as possible.
Since you are not using the dictionaries keys maybe you could just use values() along with sum():
def how_many(d):
return sum(len(v) for v in d.values())
animals = {'a': ['alpaca', 'ardvark'], 'b': ['baboon'], 'c': ['coati']}
print(how_many(animals))
Output:
4

Best way to Sort Python Dictionary by number of same Values

I have a dictionary as follows:
input = {1:'a', 2:'b', 3:'c', 4:'b', 5:'c', 6:'b'}
a - 1 time
b - 3 times
c - 2 times
I want to sort the dictionary in such a way that the value which repeats maximum times will come at fist followed by the value which repeats second most times and so on...
Desired Output
output = {1:'b', 2:'c', 3:'a'}
Please help
Thanks in advance
First of all, you need to realize you do not even care about input dict keys, only about input dict values.
real_input = input.values()
Second of all, you need to count occurences:
counted_items = collections.Counter(real_input)
Third of all, you want to iterate over them in order from most common to least common. .most_common() returns list of (key, count) tuples in expected order.
most_common_in_order = counted_items.most_common()
After that you want to convert that list to dict, with consecutive inegers as keys
result = {i: v for i, (v, _) in zip(itertools.count(1), most_common_in_order)}
Or, in concise form:
result = {i: v for i, (v, _) in zip(itertools.count(1),
collections.Counter(input.values()).most_common())}
Note that dictionaries are inherently not ordered, so actual order is implementation detail, and it's not guaranteed.
from collections import Counter
input = {1: 'a', 2: 'b', 3: 'c', 4: 'b', 5: 'c', 6: 'b'}
output = {index: value for index, (value, _)
in enumerate(Counter(input.values()).most_common(),
start=1)}
print(output)
Edited for Python 3 compatibility.
from collections import Counter
i = {1:'a', 2:'b', 3:'c', 4:'b', 5:'c', 6:'b'}
print (Counter(i.values()))
#Output
Counter({'b': 3, 'c': 2, 'a': 1})

Python3 TypeError: list indices must be integers or slices, not str

i have the task to get the String 'AAAABBBCCDAABBB' into a list like this: ['A','B','C','D','A','B']
I am working on this for 2 hours now, and i can't get the solution. This is my code so far:
list = []
string = 'AAAABBBCCDAABBB'
i = 1
for i in string:
list.append(i)
print(list)
for element in list:
if list[element] == list[element-1]:
list.remove(list[element])
print(list)
I am a newbie to programming, and the error "TypeError: list indices must be integers or slices, not str" always shows up...
I already changed the comparison
if list[element] == list[element-1]
to
if list[element] is list[element-1]
But the error stays the same. I already googled a few times, but there were always lists which didn't need the string-format, but i need it (am i right?).
Thank you for helping!
NoAbL
First of all don't name your variables after built in python statements or data structures like list, tuple or even the name of a module you import, this also applies to files. for example naming your file socket.py and importing the socket module is definitely going to lead to an error (I'll leave you to try that out by yourself)
in your code element is a string, indexes of an iterable must be numbers not strings, so you can tell python
give me the item at position 2.
but right now you're trying to say give me the item at position A and that's not even valid in English, talk-less of a programming language.
you should use the enumerate function if you want to get indexes of an iterable as you loop through it or you could just do
for i in range(len(list))
and loop through the range of the length of the list, you don't really need the elements anyway.
Here is a simpler approach to what you want to do
s = string = 'AAAABBBCCDAABBB'
ls = []
for i in s:
if ls:
if i != ls[-1]:
ls.append(i)
else:
ls.append(i)
print(ls)
It is a different approach, but your problem can be solved using itertools.groupby as follows:
from itertools import groupby
string = 'AAAABBBCCDAABBB'
answer = [group[0] for group in groupby(string)]
print(answer)
Output
['A', 'B', 'C', 'D', 'A', 'B']
According to the documentation, groupby:
Make an iterator that returns consecutive keys and groups from the iterable
In my example we use a list comprehension to iterate over the consecutive keys and groups, and use the index 0 to extract just the key.
You can try the following code:
list = []
string = 'AAAABBBCCDAABBB'
# remove the duplicate character before append to list
prev = ''
for char in string:
if char == prev:
pass
else:
list.append(char)
prev = char
print(list)
Output:
['A', 'B', 'C', 'D', 'A', 'B']
In your loop, element is the string. You want to have the index.
Try for i, element in enumerate(list).
EDIT: i will now be the index of the element you're currently iterating through.

Function that prints each element of a list and its index per line

I am trying to write a function that can print the element and index of a list. I want to do this without using the enumerate built in function and do it using for loops.
I was able to print out the element but I couldn't figure out a way to loop the index of my list.
Is there any good way I could work around this? Many thanks.
You could do this, simply iterating over the range of numbers regarding the length of your list:
def item_and_index(my_list):
for i in range(len(my_list)):
print(my_list[i], i)
This is exactly what you need, a function using for loops and not the enumerate function.
>>> L = ['a', 'b', 'c']
>>> for i in range(len(L)):
... print(i, L[i])
...
0 a
1 b
2 c
You could also try this:
i = 0
for elem in L:
print(i, elem)
i += 1

Resources