Is there an elegant/pythonic method for creating a dictionary from a nested list with enumerate, that enumerates at the sublist level rather that at the list-of-lists level?
Please consider this example:
nested_list = [["ca", "at"], ["li", "if", "fe"], ["ca", "ar"]]
I have tried to convert it into a dictionary that looks like this:
# Desired output.
# {'ca': 0, 'at': 1, 'li': 2, 'if': 3, 'fe': 4, 'ar': 5}
Here is my best attempt, but it appears to enumerate the list at the upper level and overwrite the value for duplicate keys - which is undesirable.
item_dict = {item: i for i, item in enumerate(nested_list) for item in item}
# Current output.
# {'ca': 2, 'at': 0, 'li': 1, 'if': 1, 'fe': 1, 'ar': 2}
Am I better off splitting this task into an un-nesting step, and then a dictionary comprehension step?
All insight is appreciated, thank you.
Using itertools.chain
Ex:
from itertools import chain
nested_list = [["ca", "at"], ["li", "if", "fe"], ["ca", "ar"]]
result = {}
c = 0
for i in chain.from_iterable(nested_list):
if i not in result:
result[i] = c
c += 1
print(result)
Output:
{'ca': 0, 'at': 1, 'li': 2, 'if': 3, 'fe': 4, 'ar': 5}
I think I've solved it, but it's quite ugly...
{key: i for i, key in enumerate(set([item for item in nested_list for item in item]))}
# Output.
{'if': 0, 'at': 1, 'fe': 2, 'li': 3, 'ca': 4, 'ar': 5}
Related
I have a defaultdict that is storing the count of names that appear in a list. The dictionary is as follows:
{"John": 5, "Jim": 2, "Zack": 1, "Brian": 5, "Tim": 3}
How can I efficiently return a dictionary that only contains the max value? In this case, since there is a tie, I'm looking for the final result to be:
{"John": 5, "Brian": 5}
I know I could loop through the original dictionary to accomplish this but I'm curious if there is a more pythonic way to go about it.
You can use this as a method to select only the maximum values:
dict1 = {"John": 5, "Jim": 2, "Zack": 1, "Brian": 5, "Tim": 3}
max1 = max(dict1.values())
dict2 = dict(filter(lambda elem: elem[1] == max1, dict1.items()))
print(dict2)
Basically, it first finds out the maximum value out of the dictionary and then filters out the entries which match with the highest value.
mm = {"John": 5, "Jim": 2, "Zack": 1, "Brian": 5, "Tim": 3}
def keys_with_top_values(my_dict):
return [key for (key, value) in my_dict.items() if value == max(my_dict.values())]
print(keys_with_top_values(mm))
I would like to modify a list that has 3 different numbers [0, 1, 2]
Each 0 should be replaced with either the last 1 or 2 value depending on which was most recent during the iteration.
Is it possible to create the new list using a list comprehension?
I know I can use a for loop and just record the last 1 or 2 and append the values to the new list but I prefer the most pythonic way.
list = [1, 1, 2, 1, 0, 0, 1, 0, 2, 0, 0]
new_list = [1, 1, 2, 1, 1, 1, 1, 2, 2, 2]
I was using this but then realised that after 2 0s in a sequence it would start recording 0s again.
new_list = [list[index-1] if list[index] == 0 else value for index,value in enumerate(list)]
Starting in python 3.8 you now have the walrus operator := which can assign values as part of an expression and works in list comprehensions. You just need to decide what the first value will be if the list starts with 0 since there is no previous value:
alist = [1, 1, 2, 1, 0, 0, 1, 0, 2, 0, 0]
j = 0
[j:= i if i else j for i in alist]
# [1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 2]
Perform that just with a list comprehension could be a little weird, so here my solution (without creating a new list):
my_list = [1, 1, 2, 1, 0, 0, 1, 0, 2, 0, 0]
last_not_zero = 0
for index, number in enumerate(my_list):
if number!=0:
last_not_zero = number
else:
my_list[index] = last_not_zero
print(my_list)
And you'll get:
[1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 2]
If you have a large list or you are using Pandas in your code,
import pandas as pd
s = pd.Series(list)
s.replace(0, pd.np.nan).ffill().to_list()
Output
[1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 2]
NOTE: If you are not using pandas/numpy in your code, then basic for loop would be the best way to do this. Advise using the above code only for large arrays with multiple manipulations.
How about this
last_value = 0
new_list = []
for value in list:
if value != 0:
last_value = value
new_list.append(value)
else:
new_list.append(last_value)
Depends on the condition.
In most cases, you can place the condition within the while loop in the if-else criteria statements.
If, say, you want to replace the content of a list if the previous value was
something,
size = len(list)
while True:
index = index + 1
if index!=0 and index<size:
if list[index-1]==something:
list[index] = value to be replaced
if index == (size-1):
a = 0
for a in (0, (size-1)):
print(a,":",list(a))
break
I have two lists of sets -
attribute = [{0, 1, 2, 3, 6, 7}, {4, 5}]
and
decision = [{0, 1, 2}, {3, 4}, {5}, {6, 7}]
I want -
{3, 4}
Here, {3, 4} is conflicting, as it is neither a subset of {0, 1, 2, 3, 6, 7}, nor of {4, 5}.
My code -
check = []
for i in attribute:
for j in decision:
if j.issubset(i):
check.append(j)
print(check)
for x in decision:
if not x in check:
temp = x
print(temp)
This gives me {3, 4}, but is there any easier (and/ or) faster way to do this?
You can use the following list comprehension:
[d for d in decision if not any(d <= a for a in attribute)]
This returns:
[{3, 4}]
If you want just the first set that satisfies the criteria, you can use next with a generator expression instead:
next(d for d in decision if not any(d <= a for a in attribute))
This returns:
{3, 4}
result = [i for i in decision if not [j for j in attribute if i.issubset(j)]]
result is the list of all set they are not a subset of attribute. :)
This is the compact version of :
result = []
for i in decision:
tmp_list = []
for j in attribute:
if i.issubset(j):
tmp_list.append(j)
if not tmp_list:
result.append(i)
I have a dictionary containing sets as the values, and I would like to make a union of all of these sets using a for loop. I have tried using set.union() with a for loop but I do not think this is working, any simple ways to do this iteration?
for key in self.thisDict.keys():
for otherKey in self.thisDict.keys():
if otherKey!=key:
unionSet=set.union(self.thisDict[otherKey])
The problem I think I am having is that I am not making a union of all sets. I am dealing with a lot of data so it is hard to tell. With the unionSet object I am creating, I am printing out this data and it doesn't seem anywhere as large as I expect it to be
It's fairly naive approach - create a result set, iterate over dict values and update result set with values found in current iteration. |= is an alias for set.update method.
d = {1: {1, 2, 3}, 2: {4, 5, 6}}
result = set()
for v in d.values():
result |= v
assert result == {1, 2, 3, 4, 5, 6}
A simple set comprehension will do:
>>> d = {1: {1, 2, 3}, 2: {4, 5, 6}}
>>> {element for value in d.values() for element in value}
{1, 2, 3, 4, 5, 6}
To my eye, this is more readable:
>>> from itertools import chain
>>> set(chain.from_iterable(d.values()))
{1, 2, 3, 4, 5, 6}
I was trying to print a list in backwards order using a for loop, however my code only prints of a 1, any ideas why?
sorted_list = ["jack",4,3,1,"jill",4,1,0,"bob",0,0,10,"tim",5,3,1,"sod",3,1,0]
des_list = []
for i in range(len(sorted_list)-2,-3,-1):
des_list.append(sorted_list[i-2])
des_list.append(sorted_list[i - 1])
des_list.append(sorted_list[i])
des_list.append(sorted_list[i+1])
print(des_list)
It would probably be easier to store the chunks of data into objects. That way, you can just call reversed() on the list of objects and handle the data that way.
If you want to keep the list in the format you have, you can loop through the list in "chunks", like this: for i in range(0, len(sorted_list), 4):.
So, for every 4 elements in the list, grab the chunk of of data using list slicing notation: sorted_list[i:i+4] and insert it to the front of the destination list.
Putting that together, you get this code:
sorted_list = ["jack",4,3,1,"jill",4,1,0,"bob",0,0,10,"tim",5,3,1,"sod",3,1,0]
des_list = []
for i in range(0, len(sorted_list), 4):
des_list.insert(0, sorted_list[i:i+4])
print(des_list)
That will output this: [['sod', 3, 1, 0], ['tim', 5, 3, 1], ['bob', 0, 0, 10], ['jill', 4, 1, 0], ['jack', 4, 3, 1]].
Keep in mind that that code will give you a list of lists, because sorted_list[i:i+4] returns a list, which is why it might be easier to use objects instead.
If you don't want to have a list of lists, you can reverse the sliced list, loop through each element in the reversed sliced list, and insert it one at a time to the destination list.
Doing that, you get this code:
sorted_list = ["jack",4,3,1,"jill",4,1,0,"bob",0,0,10,"tim",5,3,1,"sod",3,1,0]
des_list = []
for i in range(0, len(sorted_list), 4):
for j in reversed(sorted_list[i:i+4]):
des_list.insert(0, j)
print(des_list)
Which outputs this: ['sod', 3, 1, 0, 'tim', 5, 3, 1, 'bob', 0, 0, 10, 'jill', 4, 1, 0, 'jack', 4, 3, 1]