Simple example here:
I want to have a list which is filled with dictionaries for every type of animal.
The print should look like this:
dictlist_animals = [{'type':'horse','amount':2},
{'type':'monkey','amount':2},
{'type':'cat','amount':1},
{'type':'dog','amount':1}]
Because some animals exist more than once I've added a key named 'amount' which should count how many animals of every type exist.
I am not sure if the 'if-case' is correctly and what do I write in the 'else case'?
dictlist_animals = []
animals = ['horse', 'monkey', 'cat', 'horse', 'dog', 'monkey']
for a in animals:
if a not in dictlist_animals['type']:
dictlist_animals.append({'type': a, 'amount' : 1})
else:
#increment 'amount' of animal a
Better to use Counter. It's create dictionary where keys are elements of animals list and values are their count. Then you can use list comprehension for creating list with dictionaries:
from collections import Counter
animals_dict = [{'type': key, 'amount': value} for key, value in Counter(animals).items()]
Try below code,
dictlist_animals = []
animals = ['horse', 'monkey', 'cat', 'horse', 'dog', 'monkey']
covered_animals = []
for a in animals:
if a in covered_animals:
for dict_animal in dictlist_animals:
if a == dict_animal['type']:
dict_animal['amount'] = dict_animal['amount'] + 1
else:
covered_animals.append(a)
dictlist_animals.append({'type': a, 'amount' : 1})
print dictlist_animals
[{'amount': 2, 'type': 'horse'}, {'amount': 2, 'type': 'monkey'}, {'amount': 1, 'type': 'cat'}, {'amount': 1, 'type': 'dog'}]
You can't directly call dictlist_animals['type'] on a list because they are indexed numerically. What you can do is to store this data in an intermediate dictionary and then convert it in the data structure you want:
dictlist_animals = []
animals = ['horse', 'monkey', 'cat', 'horse', 'dog', 'monkey']
animals_count = {};
for a in animals:
c = animals_count.get(a, 0)
animals_count[a] = c+1
for animal, amount in animals_count.iteritems():
dictlist_animals.append({'type': animal, 'amount': amount})
Note that c = animals_count.get(a, 0) gets the current amount for the animal a if it is present, otherwise it returns the default value 0 so that you don't have to use an if/else statement.
You can also use defaultdict.
from collections import defaultdict
d = defaultdict(int)
for animal in animals:
d[animal]+= 1
dictlist_animals = [{'type': key, 'amount': value} for key, value in d.iteritems()]
Related
input:
animals= [['dog', 'snake', 'snake'], ['dog', 'dog', 'cat'], ['snake', 'snake', 'cat']]
animal_to_count = ['dog', 'cat']
output:
animal_found = [3, 2]
I have a list of list of strings. I want to count the number of each animals in that list of list of string.
I tried to do this with a for loop to target them individually:
def find_animals(animals: List[List]str, animals_to_count: List[str]) -> List[int]
counts = [0, 0]
for char in animals:
for s in char:
if s in animal_to_count:
counts=counts+1
return counts
Now this is the part where I am bugging, I know I am supposed to use counts so that every time the loops goes by, it add it to the count, but the problem is I don't know how to do it. When I do what is above, I get an error. This is for an assignment, and I would like to find the answer without using any built-in function (beside all of the list method, string method).
You should count "dog" and "cat" separately. Something like this:
def find_animals(animals, animals_to_count):
counts = [0]*len(animals_to_count)
for items in animals:
for item in items:
for index, animal_to_count in enumerate(animals_to_count):
if item==animal_to_count:
counts[index]+=1
return counts
[0]*len(animals_to_count) creates a list with as many zeros as there are elements in animals_to_count
this should work:
animals= [['dog', 'snake', 'snake'], ['dog', 'dog', 'cat'], ['snake', 'snake', 'cat']]
animal_to_count = ['dog', 'cat']
results = []
for sublist in animals:
results_per_animal_count = []
for count_animal in animal_to_count:
counter = 0
for animal in sublist:
if animal == count_animal:
counter += 1
results_per_animal_count.append(counter)
results.append(results_per_animal_count)
The printout of results is
[[1, 0], [2, 1], [0, 1]]
EDIT
If you want to get the aggregated list you can use:
animals= [['dog', 'snake', 'snake'], ['dog', 'dog', 'cat'], ['snake', 'snake', 'cat']]
animal_to_count = ['dog', 'cat']
results = []
for count_animal in animal_to_count:
counter = 0
for sublist in animals:
for animal in sublist:
if animal == count_animal:
counter += 1
results.append(counter)
Where the print out of results is:
[3, 2]
EDIT 2
animals= [['dog', 'snake', 'snake'], ['dog', 'dog', 'cat'], ['snake', 'snake', 'cat']]
animal_to_count = ['snake'] # here you add and remove search items
results = []
for count_animal in animal_to_count:
counter = 0
for sublist in animals:
for animal in sublist:
if animal == count_animal:
counter += 1
results.append(counter)
printout
[4]
This will return dictionary containing counts for each animal in the list.
def find_animals(animals:List[List], animals_to_count: List[str]):
d = {}
for animal in animals_to_count:
d[animal] = 0
for char in animals:
for s in char:
if s in animals_to_count:
d[s] += 1
return d
res = find_animals(animals,animals_to_count)
print('Dogs:',res['dog'],' ','Cats:',res['cat'])
I want to compare 2 lists (with dictionaries inside) and get values from the dictionaries that don't match.
So I have something like this:
list1 = [{'text': 'dog', 'number': 10},{'text': 'cat', 'number': 40},{'text': 'horse', 'number': 40}]
list2 = [{'text': 'dog'}]
And I want to get the texts that are not on both lists. Texts are the only criteria. It's not relevant if the numbers are the same or not.
The desired result would look like this:
list_notmatch = [{'text': 'cat'},{'text': 'horse'}]
If it's easier or faster, this would be OK too:
list_notmatch = [{'text': 'cat', 'number': 40},{'text': 'horse', 'number': 40}]
I've seen a similar question (Compare two lists of dictionaries in Python. Return non match) but the output it's not exactly what I need and I don't know if it's the best solution for what I need.
The real lists are quite long (there could be more than 10.000 dictionaries inside list1), so I guess I need a performant solution (or at least a not very slow one).
Order is not important.
Thanks!
The first form of output:
Take the 'text' in each dictionary as two sets, and then use the symmetric_difference method or xor operator:
>>> {d['text'] for d in list1} ^ {d['text'] for d in list2}
{'horse', 'cat'}
>>> {d['text'] for d in list1}.symmetric_difference({d['text'] for d in list2})
{'horse', 'cat'}
>>> [{'text': v} for v in _]
[{'text': 'horse'}, {'text': 'cat'}]
The two methods can be targeted to do some optimization. If operators are used, the set with shorter length can be placed on the left:
>>> timeit(lambda: {d['text'] for d in list1} ^ {d['text'] for d in list2})
0.59890600000017
>>> timeit(lambda: {d['text'] for d in list2} ^ {d['text'] for d in list1})
0.5732289999996283
If you use the symmetric_difference method, you can use generator expressions or maps to avoid explicitly creating a second set:
>>> timeit(lambda: {d['text'] for d in list1}.symmetric_difference({d['text'] for d in list2}))
0.6045051000000967
>>> timeit(lambda: {d['text'] for d in list1}.symmetric_difference(map(itemgetter('text'), list2)))
0.579385199999706
The second form of output:
A simple way to get the dictionary itself in the list is:
Create a dictionary for each list, where the key is the 'text' of each dictionary and the value is the corresponding dictionary.
The dict.keys() can use operators like sets (in Python3.10+, for lower versions, you need to manually convert them to sets.), so use twice subtraction to calculate the difference set, and then take the initial dictionary from the two large dictionaries according to the results.
>>> dict1 = {d['text']: d for d in list1}
>>> dict2 = {d['text']: d for d in list2}
>>> dict1_keys = dict1.keys() # use set(dict1.keys()) if the version of Python is not 3.10+
>>> dict2_keys = dict2.keys() # ditto
>>> [dict1[k] for k in dict1_keys - dict2_keys] + [dict2[k] for k in dict2_keys - dict1_keys]
[{'text': 'horse', 'number': 40}, {'text': 'cat', 'number': 40}]
Note that using the xor operator to directly obtain the symmetry difference here may not be an ideal method, because you also need to take the results from the large dictionary separately. If you want to use the xor operator, you can combine the two dictionaries and take values from them:
>>> list(map((dict1 | dict2).__getitem__, dict1_keys ^ dict2_keys))
[{'text': 'horse', 'number': 40}, {'text': 'cat', 'number': 40}]
in O(N+M) you can do this way
# your code goes here
list1 = [{'text': 'dog', 'number': 10},{'text': 'cat', 'number': 40},{'text': 'horse', 'number': 40}]
list2 = [{'text': 'dog'}]
matched = {}
no_match =[]
for i in list2:
matched[i['text']] = []
for i in list1:
if i['text'] in matched:
matched[i['text']].append(i)
else:
no_match.append(i)
matched = matched.values()
print(matched, no_match)
output
dict_values([[{'text': 'dog', 'number': 10}]]) [{'text': 'cat', 'number': 40}, {'text': 'horse', 'number': 40}]
I would use set arithmetics following way
list1 = [{'text': 'dog', 'number': 10},{'text': 'cat', 'number': 40},{'text': 'horse', 'number': 40}]
list2 = [{'text': 'dog'}]
texts1 = set(i['text'] for i in list1)
texts2 = set(i['text'] for i in list2)
texts = texts1.symmetric_difference(texts2)
list_notmatch1 = [{"text":i} for i in texts]
list_notmatch2 = [i for i in list1+list2 if i['text'] in texts]
print(list_notmatch1)
print(list_notmatch2)
output
[{'text': 'horse'}, {'text': 'cat'}]
[{'text': 'cat', 'number': 40}, {'text': 'horse', 'number': 40}]
Explanation: I create set from texts from each list, then use symmetric_difference which does
Return the symmetric difference of two sets as a new set.
(i.e. all elements that are in exactly one of the sets.)
then texts might be used to create 1st format or used to filter concatenation of list1 and list2 to get 2nd format.
You can try this:
list1 = [{'text': 'dog', 'number': 10},{'text': 'cat', 'number': 40},{'text': 'horse', 'number': 40}]
list2 = [{'text': 'dog'}]
result = []
for d1 in list1:
if not any(d2['text'] == d1['text'] for d2 in list2):
result.append(d1)
print(result)
Output:
[{'text': 'cat', 'number': 40}, {'text': 'horse', 'number': 40}]
I have a list of common keywords:
common_keywords = ['dog', 'person', 'cat']
And a list of dictionaries, containing keywords and sometimes the common_keywords listed above:
people = [{'name':'Bob', 'keywords': ['dog', 'dog', 'car', 'trampoline']},
{'name':'Kate', 'keywords': ['cat', 'jog', 'tree', 'flower']},
{'name':'Sasha', 'keywords': ['cooking', 'stove', 'person', 'cat']}]
I would like to count the frequency of the common_keywords for each person, so the desired output would look something like:
counts = [{'name': 'Bob', 'counts': [{'dog': 2}]},
{'name': 'Kate', 'counts': [{'cat': 1}]},
{'name': 'Sasha', 'counts': [{'person':1}, {'cat': 1}]]
I am able to use dict(Counter()) to count the keywords and filter them if they appear in the common_keywords but I am struggling with linking these counts back to the original name as shown in the desired output: counts.
Current code (I think I am slowly getting there):
freq_dict = {}
for p in people:
name = p['name']
for c in p['keywords']:
if c not in freq_dict:
freq_dict[name] = {c: 1}
else:
if c not in freq_dict[name]:
freq_dict[c] = 1
else:
freq_dict[c] +=1
You can use a list-comprehension along with collections.Counter which does exactly what you want with the nested list. -
from collections import Counter
[{'name':i.get('name'),
'keywords':[dict(Counter([j for j in i.get('keywords')
if j in common_keywords]))]} for i in people]
[{'name': 'Bob', 'keywords': [{'dog': 2}]},
{'name': 'Kate', 'keywords': [{'cat': 1}]},
{'name': 'Sasha', 'keywords': [{'person': 1, 'cat': 1}]}]
First, with the list comprehension you want to reconstruct the original list of dicts with keys separately defined along with i.get('key'). This will let to work with the nested list value for keywords.
Iterate over the list and filter only the ones in common_keywords
Pass this list into collections.Counter to get your dict
Return it as a list with a single dict inside as you expect it to be
Is there a way to rank a multiple string sets in descending order in Python by their length and if two or more lists are tied, rank the lists alphabetically?
Let's say I am given the following lists:
rankings = []
general = {'hello', 'how are you', 'good', 'thanks'}
fun = {'lowkey', 'jello', 'karaoke', 'stardown', 'hilarious', 'highkey', 'drunk', 'sports'}
subjects = {'math', 'science', 'english', 'french', 'history'}
cities = {'Rome', 'NYC', 'London', 'Toronto'}
animals = {'bird', 'elephant', 'mouse', 'dog', 'cat'}
foods = {'banana', 'fish', 'meat'}
My expected output is:
rankings = ["fun", "animals", "subjects", "cities", "general", "foods"]
Any ideas on the basic code behind this?
Thanks.
You will need to use string literals for each identifier, in order to sort by identifiers as a secondary "tie-breaker".
Please refer to the "Key Functions" section in the Python docs for sorting.
all_sets = {'general': {'hello', 'how are you', 'good', 'thanks'},
'fun': {'lowkey', 'jello', 'karaoke', 'stardown', 'hilarious', 'highkey', 'drunk', 'sports'},
'subjects': {'math', 'science', 'english', 'french', 'history'},
'cities': {'Rome', 'NYC', 'London', 'Toronto'},
'animals': {'bird', 'elephant', 'mouse', 'dog', 'cat'},
'foods': {'banana', 'fish', 'meat'}}
items = list(all_sets.items())
def sorter(dict_item):
k, v = dict_item # k is identifier and v is the set
return (-len(v), k) # sort descending by set length, ascending by identifier
sorted_items = sorted(items, key=sorter)
sorted_keys = [k for k, v in sorted_items]
The value of sorted_keys resulting:
['fun', 'animals', 'subjects', 'cities', 'general', 'foods']
I can't figure out how to create a function that is able to remove words with less than 6 characters from each list that is a value of a key of a dictionary.
I'm trying to pop each word that is less than 6 out of the list but I'm getting "TypeError: cannot unpack non-iterable int object". I don't know if the method I am using is correct.
def remove_word(words_dict):
items_list = list(words_dict.items())
for key, value in range(len(items_list) -1, -1, -1):
if len(value) < 6:
items_list.pop()
words_dict = items_list.sort()
return words_dict
words_dict = {'colours' : ['red', 'blue', 'green'],
'places' : ['america', 'china', 'malaysia', 'argentina', 'india'],
'animals' : ['lion', 'cat', 'dog', 'wolf', 'monkey',
'zebra'],
}
Should print:
1.
colours : []
places : ['america', 'malaysia', 'argentina']
animals : ['monkey']
# input data
words_dict = {'colours' : ['red', 'blue', 'green'],
'places' : ['america', 'china', 'malaysia', 'argentina', 'india'],
'animals' : ['lion', 'cat', 'dog', 'wolf', 'monkey',
'zebra'],
}
# creating a final output dictionary
#looping through each key value pair present in dictionary and adding the key
# the final dictionary and processed valeus to the corresponding key
# using lambda function, fast readable and easy to understand
result = {k:list(filter(lambda x:len(x)>=6, v)) for k,v in words_dict.items()}
print(result)
output
{'colours': [], 'places': ['america', 'malaysia', 'argentina'], 'animals': []}
Maybe not the cleanest way, this is not an effective way to do it, but it is readable and I wrote it this way so you can see the logic for it to work
In [23]: def remove_word(my_dict):
...: for key in my_dict:
...: to_delete = []
...: for values in my_dict[key]:
...: if len(values) < 6:
...: to_delete.append(values)
...: for word in to_delete:
...: my_dict[key].remove(word)
...: return my_dict
...:
...:
It will give you the desired output
In [26]: remove_word(words_dict)
Out[26]:
{'colours': [],
'places': ['america', 'malaysia', 'argentina'],
'animals': ['monkey']}
{k: [i for i in v if len(i) > 5] for k, v in words_dict.items()}
You can do this using a loop over the dict and a nested comprehension.
words_dict = {
'colours' : ['red', 'blue', 'green'],
'places' : ['america', 'china', 'malaysia', 'argentina', 'india'],
'animals' : ['lion', 'cat', 'dog', 'wolf', 'monkey','zebra'],
}
for key, lst in words_dict.items():
filtered_lst = [word for word in lst if len(word) >= 6]
print(f"{key} : {filtered_lst}")
Output for this is:
colours : []
places : ['america', 'malaysia', 'argentina']
animals : ['monkey']
Or to actually make a function that essentially removes the elements and returns the corrected dict as your code originally did then use something as follows:
def remove_words(words_dict):
return {key: [word for word in lst if len(word) >= 6]
for key, lst in words_dict.items()}
But then you'd still need to loop over them to print it correctly.
words_dict = remove_words(words_dict)
for key, lst in words_dict.items():
print(f"{key} : {lst}")
You can do this with nested loops:
for key in words_dict:
words_dict[key] = [i for i in dict[key] if len(i) >= 6]
A loop comprehension (building a new list based on criteria from the previous list) is actually the easiest way to accomplish this task, because of how python handles list iterators. You can actually put that inside a dict comprehension as well:
new_words_dict = {key: [i for i in value if len(i) >= 6] for key, value in words_dict.items()}