Divide tuple into multiple sub-tuples - Python - python

Not sure how to express this spefically in the title. But let's say I have this tuple:
[['a',2,3],['a',4,5],['b',10,20],['b',30,40],['a',5,6]]
How do I divide this tuple into two tuples:
a = [[2,3],[4,5],[5,6]]
b = [[10,20],[30,40]]

Assuming 'a' and 'b' are meaningful constants that deserve to be hard-coded, a simple solution:
arr = [['a',2,3],['a',4,5],['b',10,20],['b',30,40],['a',5,6]]
a = [l[1:] for l in arr if l[0] == 'a']
b = [l[1:] for l in arr if l[0] == 'b']
If a more generic code is needed:
from collections import defaultdict
arr = [['a',2,3],['a',4,5],['b',10,20],['b',30,40],['a',5,6]]
d = defaultdict(list)
for l in arr:
d[l[0]].append(l[1:])

You can do that using list comprehension:
>>> l = [['a',2,3],['a',4,5],['b',10,20],['b',30,40],['a',5,6]]
>>> a = [x[1:] for x in l if x[0] == "a"]
>>> a
[[2, 3], [4, 5], [5, 6]]
>>> b = [x[1:] for x in l if x[0] == "b"]
>>> b
[[10, 20], [30, 40]]
>>>
You need to iterate over the elements (sublists) in the main list. For each sublist, we check if the first item. If the first item is a or b, we then slice the sub list and only take the integer parts.

As jonrsharpe suggests, it's probably more useful to store those lists in a dictionary. It's easy to divide the sublists in the way you want using a dictionary to accumulate the sublists into lists as the values with the strings as the keys. And of course you can bind the resulting lists to separate names if you really want to.
seq = [['a', 2, 3], ['a', 4, 5], ['b', 10, 20], ['b', 30, 40], ['a', 5, 6]]
d = {}
for k, *u in seq:
d.setdefault(k, []).append(u)
print(d)
a = d['a']
b = d['b']
print(a)
print(b)
output
{'a': [[2, 3], [4, 5], [5, 6]], 'b': [[10, 20], [30, 40]]}
[[2, 3], [4, 5], [5, 6]]
[[10, 20], [30, 40]]
for k, *u in seq: binds the string in each sublist to k, storing the remainder of the sublist as u. We then append the u sublist to the appropriate list in the dictionary d.

Related

How to make the 1st element of list of lists to match the order of elements in another list?

How can I make the 1st element of list of lists to match the order of elements in another list? For example:
list1 = [3, 7, 1, 10, 4]
list2 = [[1,0],[3,2],[4,11],[7,9],[10,1]]
newlist = [[3,2],[7,9],[1,0],[10,1],[4,11]]
You can use list.index:
list1 = [3, 7, 1, 10, 4]
list2 = [[1, 0], [3, 2], [4, 11], [7, 9], [10, 1]]
newlist = sorted(list2, key=lambda l: list1.index(l[0]))
print(newlist)
Prints:
[[3, 2], [7, 9], [1, 0], [10, 1], [4, 11]]
If the lists are large, I'd suggest to create a mapping from list1 and use that in the sorting key function:
m = {v: i for i, v in enumerate(list1)}
newlist = sorted(list2, key=lambda l: m[l[0]])
print(newlist)
If the firsts in the second list are always a permutation of the first list (i.e., if that's not a misleading example):
firsts = next(zip(*list2))
first2full = dict(zip(firsts, list2))
newlist = [*map(first2full.get, list1)]
Try it online!
You can do that like this. But be careful when you have repititive first elements in list2 elements !
a = []
for i in range(len(list1)):
for j in range(len(list2)):
if list1[i] == list2[j][0]:
a.append(list2[j])
print(a)

Comparing the 1st element of list of lists to another list then adding only the lists that match to a new list

My aim is to compare the 1st element of a list of lists with another list and have the lists added to a new list if the 1st elements of both lists match. So for instance,
list1 = [2,4,7,10]
list2 = [[2,5],[3,7],[1,6],[10,3]]
newlist = [[2,5],[10,3]]
What if the list1 items does NOT have exactly sequence matching list2?
Let's assume list1 has [2, 7, 10, 4], and list2 is the same as posted sample.
Using pure zip won't get what you expect!
Alternatively, you could try this approach: (it works w/o assuming there is a matching ordering relationship between the two lists)
L = [2, 7, 4, 10]
M = [[10, 1], [3, 5], [2, 8], [4, 6], [10, 10]]
result = []
for ll in M:
if ll[0] in set(L):
result.append(ll)
result.append(ll)
print(result)
# [[10, 1], [2, 8], [4, 6], [10, 10]]
You can use zip():
newlist = []
for a, b in zip(list1, list2):
if a == b[1]:
newlist.append(b)
Or with a generator expression:
newlist = [b for a, b in zip(list1, list2) if a == b[1]]
I would use a list comprehension:
[snd for fst, snd in zip(list1, list2) if fst == snd[0]]
This outputs:
[[2, 5], [10, 3]]

adding items into sublist of list based on condition in python

I have 2 lists like below
list1=[['a',2,3],['b',4,5],['c',6,7]]
list2=[['a',5],['b',3],['c',8]]
i want to join this 2 list as 1 list based on the condition on sublist.
if 1st element of sublist in list1 is equal to 1st element of sublist in list2, then 2nd element of sublist in list2 should add to list1
here result should be
list1=[['a',2,3,5],['b',4,5,3],['c',6,7,8]]
i am able to do it for single list item like below
list1=['a',2,3]
list3=['a',5]
if list1[0]==list3[0]:
list1.extend(list3[1:])
else:
list1
print list1
help me the same in doing for sublist as mentioned above.i am unable to apply 2 for loops.
help me
This is your initial data:
>>> list1=[['a',2,3],['b',4,5],['c',6,7]]
>>> list2=[['a',5],['b',3],['c',8]]
Then, we zip your lists and we use list comprehension to generate a new list:
>>> [ a+b[1:] for a,b in zip( list1, list2 ) if a[0] == b[0] ]
Results:
[['a', 2, 3, 5], ['b', 4, 5, 3], ['c', 6, 7, 8]]
Just in one line of code :)
Edited 1
thanks but its not working if the list2=[['a',5],['c',8],['b',3]]. Help me
>>> list1=[['a',2,3],['b',4,5],['c',6,7]]
>>> list2=[['a',5],['c',8],['b',3]] # <--- other list2
Using if in different place:
>>> [ a+ ( b[1:] if a[0] == b[0] else [] ) for a,b in zip( list1, list2 ) ]
Results:
[['a', 2, 3, 5], ['b', 4, 5], ['c', 6, 7]]
Edited 2
but the 2nd and 3rd sublist in list1 has values in list2 but its not arranged in sequence as list1.
Sorting before zip.
>>> zipped_sorted_list = zip( sorted(list1), sorted(list2) )
>>> [ a+ ( b[1:] if a[0] == b[0] else [] ) for a,b in zipped_sorted_list ]
Results
[['a', 2, 3, 5], ['b', 4, 5, 3], ['c', 6, 7, 8]]
I can suggest to convert the main list to a dict:
dict1 = { e[0]:e[1:] for e in list1 }
print(dict1)
#=> {'a': [2, 3], 'c': [6, 7], 'b': [4, 5]}
Then iterate over list2 adding elements to the dict:
for e in list2:
dict1[e[0]].extend(e[1:])
#=> {'a': [2, 3], 'c': [6, 7], 'b': [4, 5]}
If you need it back as a list (to be sorted):
result = [ [k] + v for k, v in dict1.items() ]
print(result)
#=> [['a', 2, 3, 5], ['c', 6, 7, 8], ['b', 4, 5, 3]]
list1=[['a',2,3],['b',4,5],['c',6,7]]
list2=[['a',5],['b',3],['c',8]]
#Sort the lists by first element in each sublist
list1.sort(key=lambda x: x[0])
list2.sort(key=lambda x: x[0])
#this count is used with the next loop in order to keep track of comparison
#between list1's sublists with list2's sublists.
count = 0
for sublist in list1:
if sublist[0] == list2[count][0]: #comparison logic here
sublist.extend(list2[count][1:])
count += 1 #increment the count each loop
print (list1)

How to sort a list of lists based on values of another list of lists?

My two list of lists are:
lst_1 = [[1, 'John'], [2, 'Mcquin'], [4, 'Paul'], [7, 'Jimmy'], [9, 'Coco'], [11, 'Coco']]
lst_2 = [[3, 'Mcquin', 1], [6, 'Paul', 6], [5, 'John', 15], [12, 'Coco', 18], [8, 'Jimmy', 24], [10, 'Coco', 24]]
What is the most efficient way of sorting lst_1 based on the second value in the sublist of lst_2 (lst_2[i][1])?
Preferred output:
[[2, 'Mcquin'], [4, 'Paul'], [1, 'John'], [9, 'Coco'], [7, 'Jimmy'], [11, 'Coco']]
It doesn't matter if there are duplicates of the same name (Coco in this case). Also, the lists will always contain the same names like here.
If both your lists have the same amount of names, you could store the indices of each element in a collections.defaultdict, then pop off each index and use it as the sort key when an item is found during sorting.
Demo:
from collections import defaultdict, deque
lst_1 = [[1, 'John'], [2, 'Mcquin'], [4, 'Paul'], [7, 'Jimmy'], [9, 'Coco'], [11, 'Coco']]
lst_2 = [[3, 'Mcquin', 1], [6, 'Paul', 6], [5, 'John', 15], [12, 'Coco', 18], [8, 'Jimmy', 24], [10, 'Coco', 24]]
sort_map = defaultdict(deque)
for i, x in enumerate(lst_2):
sort_map[x[1]].append(i)
result = sorted(lst_1, key=lambda x: sort_map[x[1]].popleft())
print(result)
Output:
[[2, 'Mcquin'], [4, 'Paul'], [1, 'John'], [9, 'Coco'], [7, 'Jimmy'], [11, 'Coco']].
Note: You can use collections.deque to pop off elements from the beginning in constant time, as shown above. This minor improvement allows the above solution to remain at overall O(NlogN), which is the cost of sorting.
Edit: I think I have an O(n) solution!
Originally, I thought we could create a dictionary of the names and the indexes they should appear in the final list based on lst_2. Then we could create the final list by sorting lst_1 - giving an O(n log(n)) solution.
However, the problem with that method is that there are duplicate names in lst_2! Also, this new method even has a better time complexity!
First we create a dictionary based on lst_1 where each key is a name and each value is a list collections.deque (thanks RoadRunner) of the numbers which correspond to that name.
By using a deque, we maintain the ordering of those elements in lst_1 with the same names. Also, we have the ability to call .popleft on a deque in O(1) time.
This then allows us to iterate over lst_2 (removing the need for any sorting as it is already in order) and append to a new list the name followed by the first entry of values in the dictionary we created.
If we use .popleft() to get the first element, we also remove it meaning that when that name next comes up in lst_2, we get the next value in lst_1.
So, here's the code:
import collections
vals = {}
for v, n in lst_1:
vals.setdefault(n, collections.deque()).append(v)
#vals == {'Paul': [4], 'Coco': [9, 11], 'John': [1], 'Mcquin': [2], 'Jimmy': [7]}
# (each key here ^^ is actually a deque but it's easier to see with lists)
r = []
for _,n,_ in lst_2:
r.append([n, vals[n].popleft()])
giving r (for result) as:
[['Mcquin', 2], ['Paul', 4], ['John', 1], ['Coco', 9], ['Jimmy', 7], ['Coco', 11]]
Very not pythonish but still easy to understand and working:
lst_new = []
for item in lst_2:
name = item[1]
for item2 in lst_1:
if name == item2[1]:
lst_new.append(list.copy(item2))
lst_1.remove(item2)
#item2[1] = "" is also an option but it's worse for long inputs
break
Output:
>>> lst_new
[[2, 'Mcquin'], [4, 'Paul'], [1, 'John'], [9, 'Coco'], [7, 'Jimmy'], [11, 'Coco']]
Given two lists:
xs = [[4, 'a'], [3, 'b'], [7, 'c'], [10, 'd']]
ys = [ 7, 3, 4, 10]
the following line sorts the list xs by the order of items in ys:
[x for y in ys for x in xs if x[0] == y]
Result:
>>> [x for y in ys for x in xs if x[0] == y]
[[7, 'c'], [3, 'b'], [4, 'a'], [10, 'd']]
Try this:
l = sorted(lst_1, key=lambda x: [i[2] for i in lst_2 if i[1] == x[1]][0])
Explanation: We're sorting with the key being the 3rd value (i[2]) from lst_2 only if the 2nd value matches the argument (i[1] == x[1]).
Note that if a value that exist in lst_1 is missing from lst_2 an error will result (perhaps justifiably, since a key is missing).

How to make dictionary from list of lists

This is what I am doing:
Where data is a list of lists in the form of [[int1, int2, int3], [int1, int2,int3]].
I want a dictionary that looks like: {int1: [int2, int3], in2:[int2, int3]}.
I am checking what the size of data is before the dictionary comprehension and it is 1417.
I then check what the length of the dictionary is and it is something like 11, I don't know what is happening to the data list since all of the elements are not being copied into containsBacon.
def makeComprehension(data):
containsBacon = dict([(movies[2], movies[0:2]) for movies in data])
Here's a way to do it:
>>> l = [[1,2,3], [10,20,30]]
>>> d = {m[0]:m[1:] for m in l}
>>> d
{1: [2, 3], 10: [20, 30]}
Note that not all the elements will be in the resulting dictionary, because if two lists start with the same elements, this will create the same key, and will therefore not appear.
If you want to have all your original elements in your resulting dictionary, you can do the following:
>>> l = [[1,2,3], [10,20,30], [1,5,6]
>>> {m[0]:[x for n in l if n[0]==m[0] for x in n[1:]] for m in l}
{1: [2, 3, 5, 6], 10: [20, 30]}
Similar to #DevShark answer, but with destructuring assignment:
>>> L = [[1,2,3], [10,20,30], [1,5,6]]
>>> {k:v for k,*v in L}
{1: [5, 6], 10: [20, 30]}
If you want to concatenate the values for a given key, do not use a dict comprehension:
>>> d = {}
>>> for k,*v in L: d.setdefault(k, []).extend(v)
...
>>> d
{1: [2, 3, 5, 6], 10: [20, 30]}
The setdefault method creates the d[k] entry and set it to an empty list if it does not exist.
This solution is O(n) vs O(n^2) in #DevShark answer.
Here's another O(n) version:
>>> import functools
>>> functools.reduce(lambda d,m:{**d, m[0]:d.get(m[0], []) + m[1:]}, L, {})
{1: [2, 3, 5, 6], 10: [20, 30]}
d[m[0]] is updated to its previous value + m[1:]
If you want a dict comprehension, you may use itertools.groupby for a O(n lg n) solution:
>>> import itertools
>>> L.sort() # O(n lg n) part
>>> L
[[1, 2, 3], [1, 5, 6], [10, 20, 30]]
{k:[v for m in ms for v in m[1:]] for k, ms in itertools.groupby(L, lambda m:m[0])}
{1: [2, 3, 5, 6], 10: [20, 30]}

Categories

Resources