CG7Son
CG7Son

Reputation: 31

-Python- Ordering lists based on a format

I'm new to programming in general, so looking to really expand my skills here. I'm trying to write a script that will grab a list of strings from an object, then order them based on a template of my design. Any items not in the template will be added to the end.

Here's how I'm doing it now, but could someone suggest a better/more efficient way?

    originalList = ['b', 'a', 'c', 'z', 'd']
    listTemplate = ['a', 'b', 'c', 'd']
    listFinal = []

    for thing in listTemplate:
        if thing in originalList:
            listFinal.append(thing)
            originalList.pop(originalList.index(thing))

    for thing in originalList:
            listFinal.append(thing)
            originalList.pop(originalList.index(thing))

Upvotes: 3

Views: 110

Answers (5)

NPE
NPE

Reputation: 500327

Here is a way that has better computational complexity:

# add all elements of originalList not found in listTemplate to the back of listTemplate
s = set(listTemplate)
listTemplate.extend(el for el in originalList if el not in s)

# now sort
rank = {el:index for index,el in enumerate(listTemplate)}
listFinal = sorted(originalList, key=rank.get)

Upvotes: 0

CT Zhu
CT Zhu

Reputation: 54330

There is no need to create a new dictionary at all:

>>> len_lis1=len(lis1)

>>> lis1.sort(key = lambda x: lis2.index(x) if x in lis2 else len_lis1)

>>> lis1
    ['a', 'b', 'c', 'd', 'z']

Upvotes: 0

Óscar López
Óscar López

Reputation: 236004

Try this:

originalList = ['b', 'a', 'c', 'z', 'd']
listTemplate = ['a', 'b', 'c', 'd']

order = { element:index for index, element in enumerate(listTemplate) }
sorted(originalList, key=lambda element: order.get(element, float('+inf')))

=> ['a', 'b', 'c', 'd', 'z']

This is how it works:

  • First, we build a dictionary indicating, for each element in listTemplate, its relative order with respect to the others. For example a is 0, b is 1 and so on
  • Then we sort originalList. If one of its elements is present in the order dictionary, then use its relative position for ordering. If it's not present, return a positive infinite value - this will guarantee that the elements not in listTemplate will end up at the end, with no further ordering among them.

The solution in the question, although correct, is not very pythonic. In particular, whenever you have to build a new list, try to use a list comprehension instead of explicit looping/appending. And it's not a good practice to "destroy" the input list (using pop() in this case).

Upvotes: 2

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250941

You can create a dict using the listTemplate list, that way the expensive(O(N)) list.index operations can be reduced to O(1) lookups.

>>> lis1 = ['b', 'a', 'c', 'z', 'd']
>>> lis2 = ['a', 'b', 'c', 'd']

Use enumerate to create a dict with the items as keys(Considering that the items are hashable) and index as values.

>>> dic = { x:i for i,x in enumerate(lis2) }

Now dic looks like:

{'a': 0, 'c': 2, 'b': 1, 'd': 3}

Now for each item in lis1 we need to check it's index in dic, if the key is not found we return float('inf').

Function used as key:

def get_index(key):
   return dic.get(key, float('inf'))

Now sort the list:

>>> lis1.sort(key=get_index)
>>> lis1
['a', 'b', 'c', 'd', 'z']

Upvotes: 1

David Hall
David Hall

Reputation: 535

For the final step, you can just use:

listFinal += originalList

and it will add these items to the end.

Upvotes: 0

Related Questions