Python - Is there a way to append multiple values to one key? - python

I have a list of numbers and values for each number. What I want to do is check if the number already exists in a dictionary and if it does, append the value to that list of values for the specific key.
For an Example
0
a
2
b
3
c
0
d
7
e
What I want to achieve is to populate a dictionary where the numbers would be the keys and letters would be the values.However in the event that the number 0 comes up again I want to take the value of the second 0 and append it to my list of values.
Basically the outcome would be
"0" : [a,d]
"2" : [b]
"3" : [c]
"7" : [e]
Right now im in the process of the following:
num_letter_dict = {}
num = ['0', '2', '3', '0','7']
letters = ['a', 'b', 'c', 'd','e']
for line in num:
if line in num_letter_dict:
num_letter_dict[line].append(letters)
else:
num_letter_dict[line] = [letters]
print(num_letter_dict)
This is the result I am getting
{'0': [['a', 'b', 'c', 'd', 'e']]}
{'0': [['a', 'b', 'c', 'd', 'e']], '2': [['a', 'b', 'c', 'd', 'e']]}
{'0': [['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']], '2': [['a', 'b', 'c', 'd', 'e']]}
{'0': [['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']], '2': [['a', 'b', 'c', 'd', 'e']], '3': [['a', 'b', 'c', 'd', 'e']]}
{'0': [['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']], '2': [['a', 'b', 'c', 'd', 'e']], '3': [['a', 'b', 'c', 'd', 'e']], '7': [['a', 'b', 'c', 'd', 'e']]}

You're appending letters, which by itself is a full list. Instead, you want to append the element in letter that corresponds to the index of the key you're looking at in the num list.
for idx, line in enumerate(num):
if line in num_letter_dict:
num_letter_dict[line].append(letters[idx]) # append the element
else:
num_letter_dict[line] = [letters[idx]]
Result:
>>> print(num_letter_dict)
{'0': ['a', 'c'], '2': ['b'], '3': ['d'], '7': ['e']}

You just need to append the individual letter to the relevant list inside the dictionary, and not the whole list of letters. The zip function will loop over corresponding values from the two input lists as shown:
num_letter_dict = {}
num = ['0', '2', '3', '0','7']
letters = ['a', 'b', 'c', 'd','e']
for n, letter in zip(num, letters):
if n not in num_letter_dict:
num_letter_dict[n] = []
num_letter_dict[n].append(letter)
print(num_letter_dict)
Gives:
{'0': ['a', 'c'], '3': ['d'], '7': ['e'], '2': ['b']}

I think this can help you.
num_letter_dict = {}
num = ['0', '2', '0', '3','7']
letters = ['a', 'b', 'c', 'd','e']
for value, line in zip(letters,num):
if line in num_letter_dict:
num_letter_dict[line].append(value) # append the element
else:
num_letter_dict[line] = [value]

Try the following code, I have modified the for loop and used extend
for i, letter in zip(num, letters):
if i not in num_letter_dict:
num_letter_dict[i] = []
num_letter_dict[i].extend(letter) #change over here
print(num_letter_dict)

Ahh, I had to deal with this same issue yesterday. While the other submitted answers would work, might I recommend the defaultdict from the Collections module?
from collections import defaultdict
num = ['0', '2', '3', '0','7']
letters = ['a', 'b', 'c', 'd','e']
num_letter_dict = defaultdict(list)
for n, letter in zip(num, letters):
num_letter_dict[n].append(letter)
print(num_letter_dict)
I like this approach because it allows the defaultdict class to do the construction of the list internally, rather than muddying up one's own source code with if-statements.
You can test-execute this solution on IDEOne: https://ideone.com/9Dg9Rk

Related

Iterating through a list for specific instances

I have the following code:
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'], ['E', 'D', 'B'], ['E', 'D', 'C', 'B'], ['E', 'B'], ['E', 'C', 'B']]
Now, the lists inside a list represent node paths from start to end which were made using Networkx, however that is some background information. My question is more specific.
I am trying to derive the lists that only have every letter from A-E, aka it would return only the list:
paths_desired = [['E', 'D', 'A', 'C', 'B']]
If I were to have another path:
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'], ['D', 'B', 'A','C','E'], ['A', 'D', 'C', 'B']]
It would return:
paths_desired = [['E', 'D', 'A', 'C', 'B'],['D', 'B', 'A', 'C', 'E']]
My idea is a for loop that iterates through each list:
for i in pathways:
counter = 0
for j in letters:
if j in i:
counter = counter + 1;
if counter == 5:
desired_paths.append(i)
print(desired_paths)
This works, however, I want to make the loop more specific, meaning I want only lists that have the following order: ['E','D','A','C','B'], even if all the letters are present in a different list, within the paths list.
Additionally, is there a way I can upgrade my for loop, so that I wouldn't count, rather check if the letters are in there, and not more than 1 of each letter? Meaning no multiple Es, no multiple D, etc.
You can use a use a set and .issubset() like this:
def pathways(letters, paths):
ret = []
letters = set(letters)
for path in paths:
if letters.issubset(path):
ret.append(path)
return ret
letters = ['A', 'B', 'C', 'D', 'E']
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'],
['D', 'B', 'A','C','E'], ['A', 'D', 'C', 'B']]
print(pathways(letters, paths)) # => [['E', 'D', 'A', 'C', 'B'], ['D', 'B', 'A', 'C', 'E']]
Also, as a comment by ShadowRanger pointed out, the pathways() function could be shortened using filter(). Like this:
def pathways(letters, paths):
return list(filter(set(letters).issubset, paths))
letters = ['A', 'B', 'C', 'D', 'E']
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'],
['D', 'B', 'A','C','E'], ['A', 'D', 'C', 'B']]
print(pathways(letters, paths))

How to re-order and expand lists to have same size?

I am trying to re-order and extend lists of dictionary values with the right position and fill them with zeros.
myDict = {
"col-1": ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
"col-2": ['a', 'b', 'c', 'd', 'f', 'e']
}
The desired output should look like this:
myDict = {
"col-1": ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
"col-2": ['a', 'b', 'c', 'd', 'e', 'f', '0'] # '0' is zero
}
Note that values of lists are with order 'a', 'b', 'c', 'd', 'e', 'f', 'g'. The maximum list length in myDict.values() is fixed to 7 and the minimum is 6. myDict can have more than two elements.
I tried to use Python - extending and filling list with zeros to match another list but didn't know exactly how to make it work for my case.
Assuming the maximum length is 7 and values are only single characters from "a" to "g"
myDict = {
"col-1": ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
"col-2": ['a', 'b', 'c', 'd', 'e', 'f']
}
for key, value_list in myDict.items():
new_value_list = [0 for i in range(7)]
for value in value_list:
new_value_list[ord(value) - 97] = value
myDict[key] = new_value_list
print(myDict)
This should cover the cases identified in the original post and the clarification comment. The approach is, for each key of the dict, to iterate over the set of valid letters, outputting that letter (if it exists in the dict value), or '0' (if it doesn't).
myDict = {
"col-1": ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
"col-2": ['a', 'b', 'c', 'd', 'f', 'e'],
"col-3": ['a', 'c', 'f'],
"col-4": ['b', 'd']
}
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
for key, value in myDict.items():
myDict[key] = [(x if x in value else '0') for x in letters]
print(f"{key}: {myDict[key]}")
prints:
col-1: ['a', 'b', 'c', 'd', 'e', 'f', 'g']
col-2: ['a', 'b', 'c', 'd', 'e', 'f', '0']
col-3: ['a', '0', 'c', '0', '0', 'f', '0']
col-4: ['0', 'b', '0', 'd', '0', '0', '0']
seems to me that you could do this with a for-while loop:
for key in myDict.keys():
myDict[key] = sorted(myDict[key])
while len(myDict[key])<len(myDict["column1"]):
myDict[key].append('0')

How to efficiently split a list that has a certain periodicity, into multiple lists?

For example the original list:
['k','a','b','c','a','d','e','a','b','e','f','j','a','c','a','b']
We want to split the list into lists started with 'a' and ended with 'a', like the following:
['a','b','c','a']
['a','d','e','a']
['a','b','e','f','j','a']
['a','c','a']
The final ouput can also be a list of lists. I have tried a double for loop approach with 'a' as the condition, but this is inefficient and not pythonic.
One possible solution is using re (regex)
import re
l = ['k','a','b','c','a','d','e','a','b','e','f','j','a','c','a','b']
r = [list(f"a{_}a") for _ in re.findall("(?<=a)[^a]+(?=a)", "".join(l))]
print(r)
# [['a', 'b', 'c', 'a'], ['a', 'd', 'e', 'a'], ['a', 'b', 'e', 'f', 'j', 'a'], ['a', 'c', 'a']]
You can do this in one loop:
lst = ['k','a','b','c','a','d','e','a','b','e','f','j','a','c','a','b']
out = [[]]
for i in lst:
if i == 'a':
out[-1].append(i)
out.append([])
out[-1].append(i)
out = out[1:] if out[-1][-1] == 'a' else out[1:-1]
Also using numpy.split:
out = [ary.tolist() + ['a'] for ary in np.split(lst, np.where(np.array(lst) == 'a')[0])[1:-1]]
Output:
[['a', 'b', 'c', 'a'], ['a', 'd', 'e', 'a'], ['a', 'b', 'e', 'f', 'j', 'a'], ['a', 'c', 'a']]
Firstly you can store the indices of 'a' from the list.
oList = ['k','a','b','c','a','d','e','a','b','e','f','j','a','c','a','b']
idx_a = list()
for idx, char in enumerate(oList):
if char == 'a':
idx_a.append(idx)
Then for every consecutive indices you can get the sub-list and store it in a list
ans = [oList[idx_a[x]:idx_a[x + 1] + 1] for x in range(len(idx_a))]
You can also get more such lists if you take in-between indices also.
You can do this with a single iteration and a simple state machine:
original_list = list('kabcadeabefjacab')
multiple_lists = []
for c in original_list:
if multiple_lists:
multiple_lists[-1].append(c)
if c == 'a':
multiple_lists.append([c])
if multiple_lists[-1][-1] != 'a':
multiple_lists.pop()
print(multiple_lists)
[['a', 'b', 'c', 'a'], ['a', 'd', 'e', 'a'], ['a', 'b', 'e', 'f', 'j', 'a'], ['a', 'c', 'a']]
We can use str.split() to split the list once we str.join() it to a string, and then use a f-string to add back the stripped "a"s. Note that even if the list starts/ends with an "a", this the split list will have an empty string representing the substring before the split, so our unpacking logic that discards the first + last subsequences will still work as intended.
def split(data):
_, *subseqs, _ = "".join(data).split("a")
return [list(f"a{seq}a") for seq in subseqs]
Output:
>>> from pprint import pprint
>>> testdata = ['k','a','b','c','a','d','e','a','b','e','f','j','a','c','a','b']
>>> pprint(split(testdata))
[['a', 'b', 'c', 'a'],
['a', 'd', 'e', 'a'],
['a', 'b', 'e', 'f', 'j', 'a'],
['a', 'c', 'a']]

Reordering a list in a given order

I want to reorder my list in a given order,
For example I have a list of ['a', 'b', 'c', 'd', 'e', 'f', 'g']
this has an index of [0,1,2,3,4,5,6] and lets say the new ordered list would have an order of [3,5,6,1,2,4,0] which would result in ['d','f','g', 'b', 'c', 'e', 'a'].
How would you result in such code?
I thought of using for loop by doing the
for i in range(Len(list))
and after that I thought go using append or creating a new list? maybe but I'm not sure if I'm approaching this right.
All you need to do is iterate the list of indexes, and use it to access the list of elements, like this:
elems = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
idx = [3,5,6,1,2,4,0]
result = [elems[i] for i in idx]
print(result)
Output:
['d', 'f', 'g', 'b', 'c', 'e', 'a']
import numpy as np
my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
def my_function(my_list, index):
return np.take(my_list, index)
print(my_function(my_list, [3,5,6,1,2,4,0]))
Output: ['d' 'f' 'g' 'b' 'c' 'e' 'a']

How to eliminate the items in the python list step by step?

I am using a 'for loop' to eliminate the item step by step and generate a new list(feature_combination) including different combinations.
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
feature_combination.append(feature_list)
feature_combination
The ideal output should be:
[['A', 'B', 'C', 'D', 'E', 'F'],['A', 'B', 'C', 'D', 'E'],['A', 'B', 'C', 'D'],['A', 'B', 'C'],['A', 'B'],['A'], []]
But the current output is:
[[], [], [], [], [], [], []]
When I print the progress step by step:
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
print(feature_list)
I can get the following the results:
['A', 'B', 'C', 'D', 'E', 'F']
['A', 'B', 'C', 'D', 'E']
['A', 'B', 'C', 'D']
['A', 'B', 'C']
['A', 'B']
['A']
[]
So, why I cannot append these results to an empty list? What is the problem?
It's because when you call feature_combination.append(feature_list), you are appending a reference to feature_list, not the actual value of feature_list. Since feature_list is empty at the end of the for loop, all of the references to it are empty as well.
You can fix it by changing feature_combination.append(feature_list) to feature_combination.append(feature_list.copy()), which makes a copy of the list to store.
First of all, you need to pass an index into pop in order to specify which element to delete. Though I find this unesaccary, instead you could use slicing.
Below is an example of how you could accomplish your goal. This code adjusts to your desired output.
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list = feature_list[:-1]
feature_combination.append(feature_list)
print(feature_combination)
output
[['A', 'B', 'C', 'D', 'E', 'F'], ['A', 'B', 'C', 'D', 'E'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C'], ['A', 'B'], ['A'], []]
A Python variable is a symbolic name that is a reference or pointer to an object. Once an object is assigned to a variable, you can refer to the object by that name. But the data itself is still contained within the object. refer this.
This is because the feature_list points to a specific object, which keeps updating as you pop are subsequently. You are basically creating a list that contains [object, object, object ...] all pointing to the same feature_list object. As you keep popping and updating the object, the list that collects multiple instances of this same object also gets updated with this object.
Here is how you can test this happening -
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
feature_combination.append(feature_list)
print('iteration', i)
print(feature_combination) #Print the primary list after each iteration
iteration 0
[['A', 'B', 'C', 'D', 'E', 'F']]
iteration 1
[['A', 'B', 'C', 'D', 'E'], ['A', 'B', 'C', 'D', 'E']]
iteration 2
[['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D']]
iteration 3
[['A', 'B', 'C'], ['A', 'B', 'C'], ['A', 'B', 'C'], ['A', 'B', 'C']]
iteration 4
[['A', 'B'], ['A', 'B'], ['A', 'B'], ['A', 'B'], ['A', 'B']]
iteration 5
[['A'], ['A'], ['A'], ['A'], ['A'], ['A']]
iteration 6
[[], [], [], [], [], [], []]`
Notice, that after each iteration, every instance of the sublist is being updated after the pop and reflect inside the main list.
A fix
A fix is to use a slice to get and store a copy.
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
print(feature_list)
feature_combination.append(feature_list[:]) #<----
feature_combination
[['A', 'B', 'C', 'D', 'E', 'F'],
['A', 'B', 'C', 'D', 'E'],
['A', 'B', 'C', 'D'],
['A', 'B', 'C'],
['A', 'B'],
['A'],
[]]

Categories

Resources