List to nested list and dictionary - python

I have this one long list and want to convert it to a nested list and a dictionary.
L= ["a","abc","de","efg","", "b","ijk","lm","op","qr","", "c","123","45","6789"]
output:
nested list:
[["a","abc","de","efg"], ["b","ijk","lm","op","qr"], ["c","123","45","6789"]]
dictionary:
{"a":["abc","de","efg"],
"b":["ijk","lm","op","qr"], "c":["123","45","6789] }
Can anyone tell me how to do that in python?
And I can't import anything

I assume the groups are separated by the empty strings. For this you can use itertools.groupby:
from itertools import groupby
data = ["a","abc","de","efg","", "b","ijk","lm","op","qr","", "c","123","45","6789"]
nl = [list(g) for k, g in groupby(data, ''.__ne__) if k]
d = {next(g): list(g) for k, g in groupby(data, ''.__ne__) if k}
print(nl)
print(d)
Results:
[['a', 'abc', 'de', 'efg'], ['b', 'ijk', 'lm', 'op', 'qr'], ['c', '123', '45', '6789']]
{'a': ['abc', 'de', 'efg'], 'b': ['ijk', 'lm', 'op', 'qr'], 'c': ['123', '45', '6789']}
In the groupby I'm using ''.__ne__ which is the function for "not equal" of an empty string. This way it's only capturing groups of non-empty strings.
EDIT
I just read that you cannot import. Here's a solution just using a loop:
nl = [[]]
for s in data:
if s:
nl[-1].append(s)
else:
nl.append([])
And for the dict:
itr = iter(data)
key = next(itr)
d = {key: []}
while True:
try: val = next(itr)
except StopIteration: break
if val:
d[key].append(val)
else:
key = next(itr)
d[key] = []

Here's how to convert L to a nested list:
L= ["a","abc","de","efg","","b","ijk","lm","op","qr","","c","123","45","6789"]
nested_list_L = []
temp = []
for item in L:
if item != "":
temp.append(item)
else:
nested_list_L.append(temp)
temp = []
nested_list_L.append(temp)
And here's how to convert L to a dictionary:
L= ["a","abc","de","efg","","b","ijk","lm","op","qr","","c","123","45","6789"]
dict_L = {}
temp = []
key = ""
for item in L:
if len(item) == 1:
key = item
elif len(item) > 1:
temp.append(item)
else:
dict_L[key] = temp
temp = []
key = ""
dict_L[key] = temp

From my understanding, you are trying to:
Split a list by empty string, then
Convert the resulting nested list into a dictionary, using first element of each sub-list as the key and the rest as value.
You can certainly accomplish the task without any imports. To split a list, just iterate over it and build the nested list along the way:
def split(data, on):
nested = []
curr = []
for x in data:
if x == on:
nested.append(curr)
curr = []
else:
curr.append(x)
if curr != [] or data[-1:] == [on]:
nested.append(curr)
return nested
Then, again, iterate over this nested list to build your desired dictionary:
def build_dict(key_valss):
d = {}
for key_vals in key_valss:
if key_vals != []:
key = key_vals[0]
vals = key_vals[1:]
d[key] = vals
return d
Compose the two functions to get what you want:
>>> build_dict( split(data = ["a","abc","de","efg","", "b","ijk","lm","op","qr","", "c","123","45","6789"] , on = '') )
{'a': ['abc', 'de', 'efg'], 'b': ['ijk', 'lm', 'op', 'qr'], 'c': ['123', '45', '6789']}

Related

How to convert a flat list into a dictionary in python?

I have a flat list containing information of multiple variables and need to convert it into a dictionary. For example, 'a','b','c' are variable names and need to be the keys in the dictionary. The list could be split by '_' and ':'.
list_x = ['a:1_b:45_c:abc','a:2_b:24_c:def','a:4_b:78_c:xxx']
The desired output would be:
dict_x = {'a':[1,2,4],'b':[45,24,78],'c':['abc','def','xxx']}
I am not sure how to loop to get the keys for the dictionary since it is the same for all elements in the list.
lst = [y.split(":") for x in [x.split("_") for x in list_x] for y in x]
d = {x:[] for x in set([x[0] for x in lst])}
for k, v in lst:
d[k].append(v)
# Out[40]: {'a': ['1', '2', '4'], 'c': ['abc', 'def', 'xxx'], 'b': ['45', '24', '78']}
Try this method (explanation inline as code comments) -
#Function to turn a list of tuples into a dict after converting integers and keeping string types.
def convert(tup):
di = {}
for a, b in tup:
if b.isdecimal(): #convert to int if possible
b = int(b)
di.setdefault(a, []).append(b)
return di
#convert the input into a list of tuples
k = [tuple(j.split(':')) for i in list_x for j in i.split('_')]
#convert list of tuples into dict
convert(k)
{'a': [1, 2, 4], 'b': [45, 24, 78], 'c': ['abc', 'def', 'xxx']}
list_x = ['a:1_b:45_c:abc','a:2_b:24_c:def','a:4_b:78_c:xxx']
result_dict = {}
for list_element in list_x:
key_val_pair = list_element.split('_')
for key_val in key_val_pair:
key, val = key_val.split(':')
if key not in result_dict:
result_dict[key] = []
result_dict[key].append(val)
print(result_dict)
You need to ensure that your dictionary is dictionary of type string: list that is why I check if the dictionary contains the key and if it does then I push the item and if it doesn't then add a new key with a list containing only the value.
list_x = ['a:1_b:45_c:abc','a:2_b:24_c:def','a:4_b:78_c:xxx']
print(list_x)
dic_x = dict()
for x in list_x:
keyValueList = x.split('_')
for keyValue in keyValueList:
split = keyValue.split(':')
key = split[0]
value = split[1]
if key in dic_x:
dic_x[key].append(value)
else:
dic_x.update({key: [value]})
print(dic_x)
Assuming strings in your list_x always have the same format as: a:integer_b:integer_c:string, you can do this:
dict_x = {'a':[],'b':[],'c':[]}
for s in list_x:
sl = s.split('_')
dict_x['a'].append(int(sl[0][2:]))
dict_x['b'].append(int(sl[1][2:]))
dict_x['c'].append(sl[2][2:])
Maybe this can solve you problem with an easy way without being too much verbose neither compact. It's versatile so you can add as much identifier as you want but as you can see the format of them should be the same
list_x = ['a:1_b:45_c:abc','a:2_b:24_c:def','a:4_b:78_c:xxx']
dict_x ={}
for val in list_x:
elements = val.split('_')
for el in elements:
key, value = el.split(':')[0], el.split(':')[1]
if dict_x.get(key) is None: #If the key it's founded for the first time
dict_x[key] = [value]
else: #If I've already founded the key the data is being appended
dict_x[key].append(value)
print(dict_x)
As you can see the core it's the if that checks if the key founded not exists, in this case create a new array containing the first value founded; otherwise append the value to the actual array.
First split each string based on _ as delimiter and then split it based on : as delimiter, and add each item to a dict
>>> list_x = ['a:1_b:45_c:abc','a:2_b:24_c:def','a:4_b:78_c:xxx']
>>>
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for s in list_x:
... for kv in s.split('_'):
... k,v = kv.split(':')
... d[k].append(int(v) if v.isdigit() else v)
...
>>> dict(d)
{'a': [1, 2, 4], 'b': [45, 24, 78], 'c': ['abc', 'def', 'xxx']}
First, let's split the string by ':' and then '_'
list_x = ['a:1_b:45_c:abc','a:2_b:24_c:def','a:4_b:78_c:xxx']
def parse(s):
return [t.split(":") for t in s.split("_")]
parsed_to_lists = [parse(st) for st in list_x]
We now have
[[['a', '1'], ['b', '45'], ['c', 'abc']], [['a', '2'], ['b', '24'], ['c', 'def']], [['a', '4'], ['b', '78'], ['c', 'xxx']]]
we can flatten that by
flat_list = [item for sublist in parsed_to_lists for item in sublist]
flat_list
Which returns
[['a', '1'], ['b', '45'], ['c', 'abc'], ['a', '2'], ['b', '24'], ['c', 'def'], ['a', '4'], ['b', '78'], ['c', 'xxx']]
We want the result as a dictionary of lists, so let's create an empty one
from collections import defaultdict
res = defaultdict(list)
and fill it
for k,v in flat_list:
res[k].append(v)
res
defaultdict(<class 'list'>, {'a': ['1', '2', '4'], 'b': ['45', '24', '78'], 'c': ['abc', 'def', 'xxx']})

How to add index to a list inside a dictionary

I have a dictionary here:
dict = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
What is the necessary process to get the following result?
dict = {'A':['1_01','1_02','1_03','1_04','1_05'], 'B':['2_01','2_02'], 'C':['3_01','3_02','3_03','3_04']}
I have been learning python for quite a while now but dictionary is kind of new to me.
As others mentioned, refrain from using built-in keywords as variable names, such as dict. I kept it for simplicity on your part.
This is probably the most pythonic way of doing it (one line of code):
dict = {key:[x+"_0"+str(cnt+1) for cnt,x in enumerate(value)] for key,value in dict.items()}
You could also iterate through each dictionary item, and then each list item and manually change the list names as shown below:
for key,value in dict.items():
for cnt,x in enumerate(value):
dict[key][cnt] = x+"_0"+str(cnt+1)
Also, as some others have mentioned, if you want numbers greater than 10 to save as 1_10 rather than 1_010 you can you an if/else statement inside of the list comprehension...
dict = {key:[x+"_0"+str(cnt+1) if cnt+1 < 10 else x+"_"+str(cnt+1) for cnt,x in enumerate(value)] for key,value in dict.items()}
First iterate on keys.
Then loop on keys you are getting on key like for 'A' value is ['1','1','1','1','1'] then we can change the element at ['1','1','1','1','1']
enumerate() helps you iterate on index,value then index starts with zero as per your expected output add 1 to index. As you want the trailing 0 before each count we did '%02d'% (index+1)
Like this:
dict = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
for i in dict.keys(): #iterate on keys
for index,val in enumerate(dict[i]): #took value as we have key in i
element='%02d'% (index+1) #add trailing 0 we converted 1 to int 01
dict[i][index]=val+"_"+ str(element) #assign new value with converting integer to string
print(dict)
Output:
{'A': ['1_01', '1_02', '1_03', '1_04', '1_05'], 'C': ['3_01', '3_02', '3_03', '3_04'], 'B': ['2_01', '2_02']}
Use enumerate to iterate over list keeping track of index:
d = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
newd = {}
for k, v in d.items():
newd[k] = [f'{x}_0{i}' for i, x in enumerate(v, 1)]
print(newd)
Also a dictionary-comprehension:
d = {k: [f'{x}_0{i}' for i, x in enumerate(v, 1)] for k, v in d.items()}
Note: Don't name your dictionary as dict because it shadows the built-in.
d= {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
{x:[j + '_'+ '{:02}'.format(i+1) for i,j in enumerate(y)] for x,y in d.items()}
adict = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3'], 'D': '23454'}
newdict = {}
for i,v in adict.items():
if isinstance(v, list):
count = 0
for e in v:
count += 1
e += '_0' + str(count)
newdict[i] = newdict.get(i, [e]) + [e]
else:
newdict[i] = newdict.get(i, v)
print (newdict)
#{'A': ['1_01', '1_01', '1_02', '1_03', '1_04', '1_05'], 'B': ['2_01', '2_01', '2_02'], 'C': ['3_01', '3_01', '3_02', '3_03', '3_04'], 'D': '23454'}
This solution will check for whether your value in the dictionary is a list before assigning an index to it
You can use a dictcomp:
from itertools import starmap
d = {
'A': ['1', '1', '1', '1', '1'],
'B': ['2', '2'],
'C': ['3', '3', '3', '3']
}
f = lambda x, y: '%s_%02d' % (y, x)
print({k: list(starmap(f, enumerate(v, 1))) for k, v in d.items()})
# {'A': ['1_01', '1_02', '1_03', '1_04', '1_05'], 'B': ['2_01', '2_02'], 'C': ['3_01', '3_02', '3_03', '3_04']}

Combine elements of list

I have a problem with Python and hope someone can help me. I have a list, for example this one:
list = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
I want to combine the list in a way that all arrays with the same index[0] will be together, so then it will be like:
a, b, c, c1, d1
b, c1, c2
I tried something like this, but I did not get it working..
list = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
empty_list = []
for i in list:
if i not in empty_list:
empty_list.append(i)
print empty_list
Can someone help me?
You can try this :)
old_list = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
prev = None
empty_list = []
for l in old_list: # iterate through each sub list, sub list as l
if l[0] == prev:
# append your elements to existing sub list
for i in l: # iterate through each element in sub list
if i not in empty_list[-1]:
empty_list[-1].append(i)
else:
empty_list.append(l) # create new sub list
prev = l[0] # update prev
print(empty_list)
# [['a', 'b', 'c', 'c1', 'd1'], ['b', 'c1', 'c2']]
Using itertools.groupby:
from itertools import groupby
from operator import itemgetter
listt = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
grouped = [list(g) for _,g in groupby(listt,itemgetter(0))]
result = [[item for sslist in slist for item in sslist] for slist in grouped]
An OrderedDict can do most of the work:
from collections import OrderedDict
l = [['a','b','c'], ['a','c1','d1'], ['b','c1','c2']]
d = OrderedDict()
for el in l:
d.setdefault(el[0], el[0:1]).extend(el[1:])
print(d.values())
you can also try using defaultdict(list)
l = [['a','b','c'], ['a','c1','d1'], ['b','c1','c2']]
from collections import defaultdict
d_dict = defaultdict(list)
for i in l:
d_dict[i[0]].extend(i[1:])
[ list(k) + v for k, v in d_dict.items() ]
Output:
[['a', 'b', 'c', 'c1', 'd1'], ['b', 'c1', 'c2']]

Collapse a list of lists, grouping on specific element and appending other elements

How could I transform a list such as:
l=[ ['A', 'C21'], ['A','D43'],['B','D34'],['C','D45'],['C',D56']
to:
[ ['A','C21 D43'], ['B','D34'],['C','D45 D56'] ]
Where the grouping is performed according to element #0 of each sub list
and elements #1 are string concatenated within each group?
Try this :
l=[ ['A', 'C21'], ['A','D43'],['B','D34'],['C','D45'],['C','D56']]
x = {}
for a in l:
if a[0] not in x.keys():
x[a[0]] = [a[1]]
else:
x[a[0]].append(a[1])
print x
array_result = []
for keys, vals in x.iteritems():
array_result.append([keys, ' '.join(vals)])
print array_result
If the keys are contiguous then you can use itertools.groupby, eg:
from itertools import groupby
data =[ ['A', 'C21'], ['A','D43'],['B','D34'],['C','D45'],['C','D56'] ]
new_data = [[k, ' '.join(el[1] for el in g)] for k, g in groupby(data, lambda L: L[0])]
# [['A', 'C21 D43'], ['B', 'D34'], ['C', 'D45 D56']]
If not and order doesn't really matter, then:
from collections import defaultdict
dd = defaultdict(list)
for key, val in data:
dd[key].append(val)
new_data = [[k, ' '.join(v)] for k,v in dd.items()]
# [['B', 'D34'], ['C', 'D45 D56'], ['A', 'C21 D43']]
Alternatively - make use of dict.setdefault, eg:
d = {}
for key, val in data:
d.setdefault(key, []).append(val)
new_data = [[k, ' '.join(v)] for k,v in d.items()]
Or, if the keys aren't contiguous, but the output should maintain the order of the input, then use collections.OrderedDict, eg:
from collections import OrderedDict
d = OrderedDict()
for key, val in data:
d.setdefault(key, []).append(val)
new_data = [[k, ' '.join(v)] for k,v in d.items()]
# [['A', 'C21 D43'], ['B', 'D34'], ['C', 'D45 D56']]

py3k: mapping dictionary (string->number) into list (of strings)

Assume we have dictionary that translates strings into numbers.
How to reverse it into list ?
Let assume, we can fill not mapped numbers with empty string ''.
Here example how it works:
>>> dic_into_list({'x':0, 'z':2, 'w':3})
['x', '', 'z', 'w']
d = {'x':0, 'z':2, 'w':3}
lst = [""] * (max(d.values()) + 1)
for k, v in d.items():
lst[v] = k
print(lst)
prints
['x', '', 'z', 'w']
The simplest way is to flip the dict and then iterate up to the maximum value (now key) in the dict:
original = {'x':0, 'z':2, 'w':3}
d = dict((v, k) for k, v in original.iteritems())
print [d.get(i, '') for i in range(max(d) + 1)]
I share my current solution: (I look for shorter and cleared implementation in other posts):
def dic_into_list(dic):
maxindex = max([v for i,v in dic.items()])
dicrev = {num:name for name,num in dic.items()}
l=[]
for i in range(0,maxindex+1):
if i in dicrev:
l.append(dicrev[i])
else:
l.append('')
return l

Categories