Sorting a List Based on Values From Another List

A common problem people often run up against in Python is understanding a method to sort one list based on the values in another.

There are many solutions to this but I will select three main methods, one of which uses well-understood Python built-in functions such as zip() and sorted(). One will use the dictionary data type, and finally, I’ll introduce an imported module called more_itertools with its sort_together() method.

You can watch me going over the solutions in the following video tutorial as you read over the remaining article:

For the purposes of this demonstration, I’m assuming we have a small stock list from a deli that stocks international foodstuffs, and a list containing the quantity of each item. The problem we will solve is how best to sort a list of stock items ordered by ascending stock numbers, based on the stock count list.

Here is the initial data we will use in each method:

stock_items = ['Escargot', 'Nori', 'Xacuti', 'Rouladen', 'Falafel', 'Tabbouleh', 'Icaco']
stock_count = [12, 5, 8, 18, 3, 10, 4]
# Problem: We want a list of stock items sorted from lowest stock number to highest stock number

Sorting using the zip() and sorted() functions

In Python, the zip() function takes several iterables and ‘zips’ them together as tuples, formed using the first iterator from each iterable, then the second until the shortest iterable is complete. Here’s an example. We have three iterables, a, b, and c.

a = ('F', 'x', 'r', 'c', 'e')

b = ('i', 't', ' ', 'a', 'm')

c = ('n', 'e', 'A', 'd', 'y')

x = list(zip(a, b, c))

# Result

[('F', 'i', 'n'), ('x', 't', 'e'), ('r', ' ', 'A'), ('c', 'a', 'd'), ('e', 'm', 'y')]

We can then use a for loop to clean the zipped list up:

for tup in x:
     for i in tup:
        print(i, end='')

# Result

Finxter Academy

The sorted() function returns a sorted list of a specified iterable object. Here’s an example using sorted() on an unordered tuple:

y = ('a', '3', '6', 'f', '1', 'd', '4', 'b', '2', 'c', '5', 'e')

z = sorted(y)

print(z)

# Result

['1', '2', '3', '4', '5', '6', 'a', 'b', 'c', 'd', 'e', 'f']

So once we understand these functions we can apply them to our problem. First I’ll apply a rather long-winded approach to step through the stages, then I’ll show a more concise version.

Example 1 – Long version zip() and sorted()

# Example 1: Zip - Sorted

stock_items = ['Escargot', 'Nori', 'Xacuti', 'Rouladen', 'Falafel', 'Tabbouleh', 'Icaco']

stock_count = [12, 5, 8, 18, 3, 10, 4]

# Problem: We want a list of stock items sorted from lowest stock count to highest stock count

zipped_list = zip(stock_count, stock_items)
# Returns zip object <zip object at 0x00000123DFDE64C0>

sorted_list = sorted(zipped_list)
# Returns [(3, 'Falafel'), (4, 'Icaco'), (5, 'Nori'), (8, 'Xacuti'), (10, 'Tabbouleh'), (12, 'Escargot'), (18, 'Rouladen')]

new_list = [value[1] for value in sorted_list]

print(new_list)

# Result

['Falafel', 'Icaco', 'Nori', 'Xacuti', 'Tabbouleh', 'Escargot', 'Rouladen']

So we received the correct answer. In this method, we went through three steps and then a print command, so lengthy but useful for people who may need to step through the process in a logical and considered way.

Now let’s consolidate those steps into one line of code including the print command.

Example 2 – Short version zip() and sorted()

# Example 2: Concise Zip - Sorted

stock_items = ['Escargot', 'Nori', 'Xacuti', 'Rouladen', 'Falafel', 'Tabbouleh', 'Icaco']

stock_count = [12, 5, 8, 18, 3, 10, 4]

# Problem: We want the stock items sorted from lowest stock to highest stock numbers

print([stock_items for _, stock_items in sorted(zip(stock_count, stock_items))])

# Result

['Falafel', 'Icaco', 'Nori', 'Xacuti', 'Tabbouleh', 'Escargot', 'Rouladen']

Note the use of the underscore in this line of code. The underscore has many uses in Python, and in this usage, it’s in the ‘I don’t care’ mode. The stock item is the only value we need, not the stock count, so instead of using the syntax “stock_items for stock_count, stock_value in sorted….” we simply use an underscore as shown.

Sorting using the dictionary datatype

In this example we will join the two lists as a dictionary, using the stock_count value as the key and the stock_items name as the value. Now I’ve only included this method because we do not have duplicate numbers in the stock count. I’m sure everyone understands that Python dictionaries cannot have duplicate keys. If you do have duplicates, there is still a way to do this using the collections module and the defaultdict() method which effectively makes a dictionary of lists, but frankly, the work involved using defaultdict simply to sort from two lists to a dictionary then back to a list makes a mockery of the method. Yet, if you would like a coding challenge I suggest you have a try as it’s great practice. I’ve put the link to the collections and defaultdict() information at the end of this article.

Having formed the dictionary we then use the sorted() method learned previously, using the stock_count value as a key.

Finally, we extract only the stock_items name and create a list.

# Example 3: Dictionary_sort

stock_items = ['Escargot', 'Nori', 'Xacuti', 'Rouladen', 'Falafel', 'Tabbouleh', 'Icaco']

stock_count = [12, 5, 8, 18, 3, 10, 4]

# Problem: We want the stock items sorted from lowest stock to highest stock numbers

# Join two lists as a dictionary
new_dict = {stock_count[i]: stock_items[i] for i in range(len(stock_items))}
# Returns {12: 'Escargot', 5: 'Nori', 8: 'Xacuti', 18: 'Rouladen', 3: 'Falafel', 10: 'Tabbouleh', 4: 'Icaco'}

# Sort based on value
sort_dict = {k: v for k, v in sorted(new_dict.items(), key=lambda item: item[0])}
# Returns {3: 'Falafel', 4: 'Icaco', 5: 'Nori', 8: 'Xacuti', 10: 'Tabbouleh', 12: 'Escargot', 18: 'Rouladen'}

# Create a list of the sorted stock items
print([v for k, v in (sort_dict.items())])

# Result

['Falafel', 'Icaco', 'Nori', 'Xacuti', 'Tabbouleh', 'Escargot', 'Rouladen']

Introducing Python module itertools and the third party more_itertools

If you are doing a lot of work with Python iterables, you owe it to yourself to investigate the f you are doing a lot of work with Python iterables, you owe it to yourself to investigate the wonderful Python module itertools at this link. It allows you to compose memory-efficient tools for a wide range of quite complex use cases when working with iterables.

To expand on that, we have an additional third-party module called, appropriately enough, more_itertools located here.

In this example, we will import a method called sort_together() contained within the more_itertools module and we’ll apply it to our problem. It does the same as we’ve shown with our previous examples but in a very memory efficient and pythonic way. I think you’ll see the difference in the concise code employed!

# Example 4: More_Itertools

from more_itertools import sort_together

stock_items = ['Escargot', 'Nori', 'Xacuti', 'Rouladen', 'Falafel', 'Tabbouleh', 'Icaco']

stock_count = [12, 5, 8, 18, 3, 10, 4]

# Problem: We want the stock items sorted from lowest stock to highest stock numbers

print(sort_together([stock_count, stock_items])[1])

# Result

('Falafel', 'Icaco', 'Nori', 'Xacuti', 'Tabbouleh', 'Escargot', 'Rouladen')

A very tidy solution to our problem.

Now if you haven’t yet worked out why I’ve used such a strange list of international foods for my example, let’s show you the final piece of code for today’s article by adding two lines to the first example of code we began the article with. Honestly, I did this to be sure that each example of code gave the desired result by quickly scanning a familiar word rather than laboriously working through the two lists.

# Example 1: Zip - Sorted

stock_items = ['Escargot', 'Nori', 'Xacuti', 'Rouladen', 'Falafel', 'Tabbouleh', 'Icaco']

stock_count = [12, 5, 8, 18, 3, 10, 4]

# Problem: We want a list of stock items sorted from lowest stock number to highest stock number

# .  .  .  .

new_list = [value[1] for value in sorted_list]
print(new_list)

for i in new_list:
    print(i[0], end='')

# Result

FINXTER

In Summary

We opened this article by posing the problem of needing to sort one list based on the values in another. We used an example where the first list was of stock items in our international deli, and the second list was the latest stock count of these items. Our aim was to return a list of stock items starting with the lowest stock numbers and ascending to the highest.

We introduced the Python functions zip() and sorted() before applying them in two examples. In the first example, we walked through the steps involved in using the two functions before consolidating the code into a one-liner.

We then looked at using the dictionary data type, on the understanding that such a use case only works when the values that will be used as keys in the dictionary have no duplicates.

If you do have duplicate keys we need to investigate the collections module in Python and utilize the defaultdict() method to create a dictionary of lists. Certainly not an elegant, memory-efficient, or time-efficient solution to our problem but an interesting coding challenge nonetheless. Details may be found here.

Finally, we mentioned the Python module itertools before introducing its third-party cousin, more_itertools and its method sort_together().

I trust this article was helpful and many thanks for reading it!