2

I have the following list:

lines
['line_North_Mid', 'line_South_Mid',
 'line_North_South', 'line_Mid_South',
 'line_South_North','line_Mid_North' ]

I would like to couple them in a tuple list as follows, with respect to their names:

tuple_list
[('line_Mid_North', 'line_North_Mid'),
 ('line_North_South', 'line_South_North'),
 ('line_Mid_South', 'line_South_Mid')]

I thought maybe I could do a string search in the elements of the lines but it wont be efficient. Is there a better way to order lines elements in a way which would look like tuple_list

Paring Criteria:

If the both elements have the same Area_name: ('North', 'Mid', 'South')

E.g.: 'line_North_Mid' should be coupled with 'line_Mid_North'

oakca
  • 1,408
  • 1
  • 18
  • 40
  • Is the ordering or your list reliable, i.e. adjacent items are always related? – jpp Jan 15 '19 at 14:19
  • What exactly is the paring criteria? How do you know which string goes with what other? – Ralf Jan 15 '19 at 14:20
  • no the only related thing is if `'line_Mid_South'` is in the list, the `'line_South_Mid'` is also exists somewhere in the list – oakca Jan 15 '19 at 14:20
  • Is the start of the string (`line_`) always the same? Do the string always have 3 parts separated by underscores? – Ralf Jan 15 '19 at 14:22
  • @Ralf it is always starting with `'line_'` and there is always `'_'` between `'South'` or `'North'` or `'Mid'` – oakca Jan 15 '19 at 14:23

4 Answers4

2

An order-agnostic O(n) solution is possible using collections.defaultdict. The idea is to use as our dictionary keys the last 2 components of your strings delimited by '_', appending values from your input list. Then extract values and convert to a list of tuples.

from collections import defaultdict

L = ['line_North_Mid', 'line_South_Mid',
     'line_North_South', 'line_Mid_South',
     'line_South_North', 'line_Mid_North']

dd = defaultdict(list)
for item in L:
    dd[frozenset(item.rsplit('_', maxsplit=2)[1:])].append(item)

res = list(map(tuple, dd.values()))

# [('line_North_Mid', 'line_Mid_North'),
#  ('line_South_Mid', 'line_Mid_South'),
#  ('line_North_South', 'line_South_North')]
jpp
  • 159,742
  • 34
  • 281
  • 339
2

Try this:

from itertools import combinations

tuple_list = [i for i in combinations(lines,2) if i[0].split('_')[1] == i[1].split('_')[2] and i[0].split('_')[2] == i[1].split('_')[1]]

or I think this is better:

[i for i in combinations(lines,2) if i[0].split('_')[1:] == i[1].split('_')[1:][::-1]]
Mehrdad Pedramfar
  • 10,941
  • 4
  • 38
  • 59
  • @oakca i Updated my answer and added a better solution. – Mehrdad Pedramfar Jan 15 '19 at 14:34
  • it looks fine and actually 1 line code so I prefer this. But there is a question: does this work if I move the place of the elements in the lines list? @Mehrdad Pedramfar – oakca Jan 15 '19 at 14:35
  • @oakca Sure, it will check all of the possible combinations of your list elements. – Mehrdad Pedramfar Jan 15 '19 at 14:37
  • This solution has quadratic complexity. The problem is solvable in linear time. There's no need to sequentially compare every string *with every other other*, e.g. see the `dict`-based solution. – jpp Jan 15 '19 at 14:46
1

You can use the following list comprehension:

lines = ['line_Mid_North', 'line_North_Mid',
         'line_North_South', 'line_South_North',
         'line_Mid_South', 'line_South_Mid']

[(j,i) for i in lines for j in lines if j not in i 
       if set(j.split('_')[1:]) < set(i.split('_'))][::2]

[('line_Mid_North', 'line_North_Mid'),
 ('line_North_South', 'line_South_North'),
 ('line_Mid_South', 'line_South_Mid')]
yatu
  • 86,083
  • 12
  • 84
  • 139
1

I suggest you have a function that returns the same key for string that are supposed to be together (a grouping-key).

def key(s):
    # ignore first part and sort other 2 parts, so they will always be in same order
    _, part_1, part_2 = s.split('_')
    return tuple(sorted([part_1, part_2]))

The you have to use some grouping method; I used defaultdict for example:

import collections

lines = [
    'line_North_Mid', 'line_South_Mid',
    'line_North_South', 'line_Mid_South',
    'line_South_North','line_Mid_North',
]

dd = collections.defaultdict(list)
for s in lines:
    dd[key(s)].append(s)     # those with same key get grouped

print(list(tuple(v) for v in dd.values()))
# [
#     ('line_North_Mid', 'line_Mid_North'),
#     ('line_South_Mid', 'line_Mid_South'),
#     ('line_North_South', 'line_South_North'),
# ]
Ralf
  • 16,086
  • 4
  • 44
  • 68