Get a dict from a python list - python

I have a list my_list = ['a', 'b', 'c', 'd'] and I need to create a dictionary which looks like
{ 'a': ['a', 'b', 'c', 'd'],
'b': ['b', 'a', 'c', 'd'],
'c': ['c', 'a', 'b', 'd'],
'd': ['d', 'a', 'b', 'c'] }
each element as its value being the list but the first element is itself.
Here is my code
my_list = ['1', '2', '3', '4']
my_dict=dict()
for x in my_list:
n = my_lst[:]
n.remove(x)
n= [x] + n[:]
my_dict[x] = n
print my_dict
which gives
{'1': ['1', '2', '3', '4'],
'3': ['3', '1', '2', '4'],
'2': ['2', '1', '3', '4'],
'4': ['4', '1', '2', '3']}
as required.
But I don't think that's the most optimal way of doing it. Any help to optimize will be appreciated.

>>> seq
['a', 'b', 'c', 'd']
>>> {e: [e]+[i for i in seq if i != e] for e in seq}
{'a': ['a', 'b', 'c', 'd'],
'b': ['b', 'a', 'c', 'd'],
'c': ['c', 'a', 'b', 'd'],
'd': ['d', 'a', 'b', 'c']}

A faster approach (than the accepted answer) for larger lists is
{e: [e] + seq[:i] + seq[i+1:] for i, e in enumerate(seq)}
Relative timings:
In [1]: seq = list(range(1000))
In [2]: %timeit {e: [e]+[i for i in seq if i != e] for e in seq}
10 loops, best of 3: 40.8 ms per loop
In [3]: %timeit {e: [e] + seq[:i] + seq[i+1:] for i, e in enumerate(seq)}
100 loops, best of 3: 6.03 ms per loop

You can get hacky with a dictionary comprehension:
my_dict = {elem: list(sorted(my_list, key=lambda x: x != elem)) for elem in my_lst}
This works on the fact that the sorted function performs a stable sort, and False is less than True
Edit: This method is less clear and probably slower, use with caution.

Related

Access next element in a for loop in python

I have a list of lists, let's call it foo
foo = [['A', '1'], ['C', '5', 'D', '9', 'E'], ['F'], ['G', 'H']]
and another list bar
bar = ['A', 'C', 'D', 'F', 'H']
I wanted search the elements of bar in foo and if they are find then the next element is to be picked and create a dictionary with keys as elements of bar.
What I tried:
pay = {}
for i in bar:
for j in foo:
for k in j:
if i == k:
try:
pay[i] = k+1
except:
pay[i] = '-'
Expected Output:
pay = {'A':1,
'C':5,
'D':9,
'F':'-',
'H':'-'}
You've missed,
Accessing the proper index to find the next element.
Using == operator instead of in property to check the value's presence in the list.
Solution:
bar = ['A', 'C', 'D', 'F', 'H']
foo = [['A', '1'], ['C', '5', 'D', '9', 'E'], ['F'], ['G', 'H']]
output_dict = {}
for element in foo:
for foo_element in element:
if foo_element in bar:
try:
output_dict[foo_element] = element[element.index(foo_element) + 1]
except:
output_dict[foo_element] = '-'
print(output_dict)
Output:
{'D': '9', 'H': '-', 'F': '-', 'C': '5', 'A': '1'}
I would normalize the data, i.e. flatten the foo and convert it to a dict of all pairs {item: next_item}.
Then just get the data from this dict.
foo = [['A', '1'], ['C', '5', 'D', '9', 'E'], ['F'], ['G', 'H']]
bar = ['A', 'C', 'D', 'F', 'H']
pdict = {k:v for s in foo for k, v in zip(s, s[1:])}
# print(pdict) # uncomment to check
pay = {k: pdict.get(k, '-') for k in bar}
print(pay) # {'A': '1', 'C': '5', 'D': '9', 'F': '-', 'H': '-'}
As #kuro mentioned, you can use enumerate function or else you can simply iterate over the the indices as below.
foo = [['A', '1'], ['C', '5', 'D', '9', 'E'], ['F'], ['G', 'H']]
bar = ['A', 'C', 'D', 'F', 'H']
pay = {}
for i in bar:
for j in foo:
for k in range(len(j)):
if i == j[k]:
try:
pay[i] = j[k + 1]
except IndexError:
pay[i] = '-'
print(pay)
Output:
{'A': '1', 'C': '5', 'D': '9', 'F': '-', 'H': '-'}
list_of_lists = foo
flattened = [val for sublist in list_of_lists for val in sublist] + ['-']
pay = {}
for bar_val in bar:
pos = flattened.index(bar_val)
try:
pay[bar_val] = int(flattened[pos+1])
except ValueError as e:
pay[bar_val] = '-'
print(pay)
Output:
{'A': 1, 'C': 5, 'D': 9, 'F': '-', 'H': '-'}
You can try to do it this way as well:
foo = [['A', '1'], ['C', '5', 'D', '9', 'E'], ['F'], ['G', 'H']]
bar = ['A', 'C', 'D', 'F', 'H']
pay = dict()
# loop through bar
for i in range(len(bar)):
# loop through foo
for j in range(len(foo)):
# loop through elements of foo
for k in range(len(foo[j])):
# check if data in foo matches element in bar
if bar[i] == foo[j][k]:
# check if data in foo is the last element in the sub-list
if (len(foo[j]) > k+1):
pay[bar[i]] = foo[j][k+1]
else:
pay[bar[i]] = '-'
else:
pass
# iterate through dict and print its contents
for k, v in pay.items():
print(k, v)
def get_next(elt, sublist):
try:
return sublist[sublist.index(elt) + 1]
except:
return '-'
final_dict = { elt: get_next(elt, sub) for sub in foo for elt in sub if elt in bar}
print(final_dict) # {'A': '1', 'C': '5', 'D': '9', 'F': '-', 'H': '-'}
Just another solution using generators:
foo = [['A', '1'], ['C', '5', 'D', '9', 'E'], ['F'], ['G', 'H']]
from itertools import chain
def get_pairs(it):
stack = None
while True:
try:
x = stack if stack else next(it)
try:
y = next(it)
if y.isdigit():
yield x, y
stack = None
else:
yield x, "-"
stack = y
except StopIteration:
yield x, "-"
break
except StopIteration:
break
result = dict([item for item in get_pairs(chain.from_iterable(foo))])
print(result)
Which yields
{'A': '1', 'C': '5', 'D': '9', 'E': '-', 'F': '-', 'G': '-', 'H': '-'}

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

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

Creating a tree diagram from list of lists

I have a 2D list:
# # # ^ # ^ # ^
l = [['A', '1', '2'], ['B', 'xx', 'A'], ['C', 'B', 's'], ['D', 'd', 'B']]
and the first element in each list can be treated as an #ID string (in the example: A, B, C, D). Anywhere where the ID's (A, B, C, D) occur in the second dimension's lists I would like to replace it with the content of the actual list. Example: ['B', 'xx', 'A'] should become ['B', 'xx', ['A', '1', '2']] because A is an #ID (first string of list) and it occurs in the second list. Output should be:
n = [['A', '1', '2'], ['B', 'xx', ['A', '1', '2']], ['C', ['B', 'xx', ['A', '1', '2']], 's'],
['D', 'd', ['B', 'xx', ['A', '1', '2']]]]
The problem I am facing is that there can be longer lists and more branches so it's getting complicated. In the end I am trying to build a tree diagram. I was thinking of calculting first what is the highest branching but don't have a solution in mind yet.
l = [['A', '1', '2'], ['B', 'xx', 'A'], ['C', 'B', 's'], ['D', 'd', 'B']]
dic = {i[0]:i for i in l}
for i in l:
fv = i[0]
for j, v in enumerate(i):
if v in dic and j!=0:
dic[fv][j] = dic[v]
res = [v for i,v in dic.items()]
print(res)
output
[['A', '1', '2'],
['B', 'xx', ['A', '1', '2']],
['C', ['B', 'xx', ['A', '1', '2']], 's'],
['D', 'd', ['B', 'xx', ['A', '1', '2']]]]
Have you tried using a dictionary? If you have the ID's then you could possibly refer to them and then loop through the array and change entries. Below is what I had
l = [['A', '1', '2'], ['B', 'xx', 'A'], ['C', 'B', 's'], ['D', 'd', 'B'], ['E', 'C', 'b']]
dt = {}
for i in l:
dt[i[0]] = i
for i in range(len(l)):
for j in range(1, len(l[i])):
if(l[i][j] in dt):
l[i][j] = dt.get(l[i][j])
print(l)
Another more succinct version:
d = {item[0]: item for item in l}
for item in l:
item[1:] = [d.get(element, element) for element in item[1:]]

Taking elements two by two from lists

I have lists like:
['a', '2', 'b', '1', 'c', '4']
['d', '5', 'e', '7', 'f', '4', 'g', '6']
And I want to make a dictionary consist of keys as letters and values as numbers. I mean:
{'a': 2, 'b': 1, 'c': 4, 'd':5, 'e':7, 'f':4, 'g':6}
You can try:
>>> l = ['a', '2', 'b', '1', 'c', '4']
>>> it = iter(l)
>>> dict(zip(it, it))
{'a': '2', 'c': '4', 'b': '1'}
First you create an iterator out of the list. Then with zip of the iterator with itself you take pair of values from the list. Finally, with dict you transform these tuples to your wanted dictionary.
If you also want to do the string to number conversion, then use:
{x: int(y) for x, y in zip(it, it)}
EDIT
If you don't want to use zip then:
{x: int(next(it)) for x in it}
l = ['a', '2', 'b', '1', 'c', '4']
d = {k:v for k,v in zip(l[::2], l[1::2])}
Or if you want the numbers to be actual numbers:
l = ['a', '2', 'b', '1', 'c', '4']
d = {k:int(v) for k,v in zip(l[::2], l[1::2])}
Use float(v) instead of int(v) if the numbers have the potential to be floating-point values instead of whole numbers.
Without using any built-in functions:
l = ['a', '2', 'b', '1', 'c', '4']
d = {}
l1 = l[::2]
l2 = l[1::2]
idx = 0
while 1:
try:
d[l1[idx]] = l2[idx]
idx += 1
except IndexError:
break
You can split the lists into two, one containing the letters and the other containing the keys, with
key_list = old_list[::2]
value_list = old_list[1::2]
Then you can loop over the two lists at once with zip and you can make the dictionary.

How to count the number of times a certain pattern in a sublist occurs within a list and then append that count to the sublist?

The challenge is that I want to count the number of times a certain pattern of items occurs in a sub-list at certain indices.
For example, I'd like to count the number of times a unique patter occurs at index 0 and index 1. 'a' and 'z' occur three times below at index 0 and index 1 while '1' and '2' occur two times below at index 0 and index 1. I'm only concerned at the pair that occurs at index 0 and 1 and I'd like to know the count of unique pairs that are there and then append that count back to the sub-list.
List = [['a','z','g','g','g'],['a','z','d','d','d'],['a','z','z','z','d'],['1','2','f','f','f'],['1','2','3','f','f'],['1','1','g','g','g']]
Desired_List = [['a','z','g','g','g',3],['a','z','d','d','d',3],['a','z','z','z','d',3],['1','2','f','f','f',2],['1','2','3','f','f',2],['1','1','g','g','g',1]]
Currently, my attempt is this:
from collections import Counter
l1 = Counter(map(lambda x: (x[0] + "|" + x[1]),List)
Deduped_Original_List = map(lambda x: Counter.keys().split("|"),l1)
Counts = map(lambda x: Counter.values(),l1)
for ele_a, ele_b in zip(Deduped_Original_List, Counts):
ele_a.append(ele_b)
This clearly doesn't work because in the process I lose index 2,3, and 4.
You can use list comprehension with collections.Counter:
from collections import Counter
lst = [['a','z','g','g','g'],['a','z','d','d','d'],['a','z','z','z','d'],['1','2','f','f','f'],['1','2','3','f','f'],['1','1','g','g','g']]
cnt = Counter([tuple(l[:2]) for l in lst])
lst_output = [l + [cnt[tuple(l[:2])]] for l in lst]
print lst_output
Ouput:
[['a', 'z', 'g', 'g', 'g', 3], ['a', 'z', 'd', 'd', 'd', 3], ['a', 'z', 'z', 'z', 'd', 3], ['1', '2', 'f', 'f', 'f', 2], ['1', '2', '3', 'f', 'f', 2], ['1', '1', 'g', 'g', 'g', 1]]
>>> import collections
>>> List = [['a','z','g','g','g'],['a','z','d','d','d'],['a','z','z','z','d'],['1','2','f','f','f'],['1','2','3','f','f'],['1','1','g','g','g']]
>>> patterns = ['az', '12']
>>> answer = collections.defaultdict(int)
>>> for subl in List:
... for pattern in patterns:
... if all(a==b for a,b in zip(subl, pattern)):
... answer[pattern] += 1
... break
...
>>> for i,subl in enumerate(List):
... if ''.join(subl[:2]) in answer:
... List[i].append(answer[''.join(subl[:2])])
...
>>> List
[['a', 'z', 'g', 'g', 'g', 3], ['a', 'z', 'd', 'd', 'd', 3], ['a', 'z', 'z', 'z', 'd', 3], ['1', '2', 'f', 'f', 'f', 2], ['1', '2', '3', 'f', 'f', 2], ['1', '1', 'g', 'g', 'g']]
>>>
I like the Counter approach of YS-L. Here is another approach:
>>> List = [['a','z','g','g','g'], ['a','z','d','d','d'], ['a','z','z','z','d'],['1','2','f','f','f'], ['1','2','3','f','f'], ['1','1','g','g','g']]
>>> d = {}
>>> for i in List:
key = i[0] + i[1]
if not d.get(key, None): d[key] = 1
else: d[key] += 1
>>> Desired_List = [li + [d[li[0] + li[1]]] for li in List]
>>> Desired_List
[['a', 'z', 'g', 'g', 'g', 3], ['a', 'z', 'd', 'd', 'd', 3], ['a', 'z', 'z', 'z', 'd', 3], ['1', '2', 'f', 'f', 'f', 2], ['1', '2', '3', 'f', 'f', 2], ['1', '1', 'g', 'g', 'g', 1]]

Categories

Resources