0

I have a list of 3-tuples, and I want to split them on the last coordinate. So for example if the input were:

[(1,2,x), (2,3,x), (1,2,z), (2,20,z)]

I'd want the output to be

[(1,2,x),(2,3,x)],[(1,2,z),(2,20,z)]

My current approach involves getting all N distinct 3rd indices and looping over the list N times to build the list of lists. I am wondering if there is a pythonic way to do this without looping multiple times.

GTOgod
  • 93
  • 7
  • I suppose this should be a 2D array instead of 1D? –  Mar 20 '22 at 01:35
  • nested list comprehension `third_indices = ['x','z']` `[[a1 for a1 in a if a1[-1]==ix] for ix in third_indices]` might be a bit better (`a`is your list) – konstanze Mar 20 '22 at 01:36
  • Its a 1D List filled with tuples of dimension 3. – GTOgod Mar 20 '22 at 01:36
  • Your example has the elements already ordered by the third element. Is that always the case? Is this possible: `[(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z'), (3,4,'x')]` if so, what is the result? – Mark Mar 20 '22 at 01:39
  • 1
    @Mark No, there's no condition on grouping, and I don't know how many distinct 3rd elements there are apriori. The result on your example is [(1,2,x), (2,3,x), (3,4,x)], [(1,2,z),(2,20,z)] – GTOgod Mar 20 '22 at 01:40
  • The result is: [(1,2,x), (2,3,x), (3,4,x)], [(1,2,z),(2,20,z)]. That's two groups -- one list for each third element (in this case x and z). – GTOgod Mar 20 '22 at 01:42

4 Answers4

2

here is one way with complexity of O(n):

lt = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z')]

res = {}
for i in lt:
  res.setdefault(i[2], []).append(i)

print(list(res.values()))

output:

>>>
[[(1, 2, 'x'), (2, 3, 'x')], [(1, 2, 'z'), (2, 20, 'z')]]
eshirvana
  • 23,227
  • 3
  • 22
  • 38
  • This solution seems to be the best. Is there a way to post-sort the lists based on the third tuple term? – GTOgod Mar 20 '22 at 01:51
  • 1
    Nvm solved, see: https://stackoverflow.com/questions/4174941/how-to-sort-a-list-of-lists-by-a-specific-index-of-the-inner-list – GTOgod Mar 20 '22 at 01:53
1

nested list comprehension if you dont know third_indicesa priori:

my_list = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z')]
third_indices = set([ix[-1] for ix in my_list])
[[item for item in my_list if item[-1]==ix] for ix in third_indices]
konstanze
  • 511
  • 3
  • 12
  • This works thanks! Is there a way to sort the final result based on the third tuple elements, i.e. have the 'x' list appear before the 'z' list. – GTOgod Mar 20 '22 at 01:47
  • 1
    @GTOgod this is complexity of O(n2) which is not optimal – eshirvana Mar 20 '22 at 01:48
1

Similar to @eshirvana, but using defaultdict, which is very performant and common for this type of problem. I would make a defaultdict and save lists of tuples based the key determined by the last element. Then all your groups will be in the values()

from collections import defaultdict

l = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z'), (3,4,'x')]

groups = defaultdict(list)

for t in l:
    groups[t[-1]].append(t)
    
list(groups.values())
# [[(1, 2, 'x'), (2, 3, 'x'), (3, 4, 'x')], [(1, 2, 'z'), (2, 20, 'z')]]
Mark
  • 90,562
  • 7
  • 108
  • 148
0

You can do something like this:

my_list = [(1,2,x), (2,3,x), (1,2,z), (2,20,z)]

list_x = [item for item in my_list if item[2] == x]
list_y = [item for item in my_list if item[2] == y]
list_z = [item for item in my_list if item[2] == z]

Edit: If x,y and z are not numbers then this should work:

my_list = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z')]

list_x = [item for item in my_list if item[2] == 'x']
list_y = [item for item in my_list if item[2] == 'y']
list_z = [item for item in my_list if item[2] == 'z']

If you want them in the same list then do this

final_list = list((list_x,list_z))
Jhanzaib Humayun
  • 1,193
  • 1
  • 4
  • 10