4

I have two lists of strings.

list_one = ["c11", "a78", "67b"]
list_two = ["a", "b", "c"]

What is the shortest way of sorting list_one using strings from list_two to get the following output?

["a78", "67b", "c11"]

Edit 1: There is a similar question Sorting list based on values from another list?, but in that question he already has the list of required indexes for resulting string, while here I have just the list of substrings.

Edit 2: Since the example of list above might be not fully representative, I add another case.

list_one is ["1.cde.png", "1.abc.png", "1.bcd.png"] list_two is ["abc", "bcd", "cde"]. The output is supposed to be [ "1.abc.png", "1.bcd.png", "1.cde.png"]

If, for example, list_one is shorter than list_two, it should still work:

list_one is ["1.cde.png", "1.abc.png"] list_two is ["abc", "bcd", "cde"] The output is supposed to be [ "1.abc.png", "1.cde.png"]

10
  • 2
    Possible duplicate of Sorting list based on values from another list? Commented Jul 19, 2018 at 9:07
  • Your expected output doesn't make sense. Why is 67b second? Commented Jul 19, 2018 at 9:09
  • what is the relation between list_one and list_two? It seems list_one's objects are included list_two's values. Is it the criteria? Commented Jul 19, 2018 at 9:11
  • @TomWojcik in stackoverflow.com/questions/6618515/… he already has the list of required order for resulting string, while here I have just the list of substrings Commented Jul 19, 2018 at 9:11
  • @blhsing because in the list_two, substring "b" is the second Commented Jul 19, 2018 at 9:12

4 Answers 4

4
key = {next((s for s in list_one if v in s), None): i for i, v in enumerate(list_two)}
print(sorted(list_one, key=key.get))

This outputs:

['a78', '67b', 'c11']
Sign up to request clarification or add additional context in comments.

6 Comments

Your code doesn't work if list_one is smaller than list_two. It gives an error "IndexError: list index out of range"
Indeed. Fixed then.
Why key=key.__getitem__ instead of key=key.get?
I'm not sure what exactly you have changed, but it still doesn't work for the case when list_one is shorter than list_two. For example, I tested your code for the case when list_one = ["c11", "a78"], list_two = ["a", "b", "c"]. The output is supposed to be ["a78", "c11"]. But it gives me an error "IndexError: list index out of range".
@tobias_k Good point. __getitem__ was entirely unnecessary. Revised as suggested. Thanks.
|
3

Try this

list_one = ["c11", "a78", "67b"]
list_two = ["a", "b", "c"]

[x for y in list_two for x in list_one if y in x]

Output :

["a78", "67b", "c11"]

1 Comment

if list_one has itens without "a", "b" or "c", and you want to keep them at the end of the output list, add this: OUTPUT_LIST + list(set(list_one) - set(OUTPUT_LIST))
1

Assuming that each item in list_one contains exactly one of the characters from list_two, and that you know the class of those characters, e.g. letters, you can extract those using a regex and build a dictionary mapping the characters to the element. Then, just look up the correct element for each character.

>>> list_one = ["c11", "a78", "67b"]
>>> list_two = ["a", "b", "c"]
>>> d = {re.search("[a-z]", s).group(): s for s in list_one}
>>> list(map(d.get, list_two))
['a78', '67b', 'c11']
>>> [d[c] for c in list_two]
['a78', '67b', 'c11']

Other than the other approaches posted so far, which all seem to be O(n²), this is only O(n).

Of course, the approach can be generalized to e.g. more than one character, or characters in specific positions of the first string, but it will always require some pattern and knowledge about that pattern. E.g., for your more recent example:

>>> list_one = ["1.cde.png", "1.abc.png", "1.bcd.png"]
>>> list_two = ["abc", "cde"]
>>> d = {re.search("\.(\w+)\.", s).group(1): s for s in list_one}
>>> d = {s.split(".")[1]: s for s in list_one}  # alternatively without re
>>> [d[c] for c in list_two if c in d]
['1.abc.png', '1.cde.png']

Comments

-1
>>> sorted(list_one, key=lambda x: [i for i,e in enumerate(list_two) if e in x][0])
['a78', '67b', 'c11']

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.