Lists / Sorting a List

Hi!

I have a sample list with 11 objects.
I want to get:

  1. Sorted list by LIKES
  2. Most popular actions (ex. LIKE, or SAVE, etc).

Pleas help me.

from typing import List

logs: List = [
    ['30-MAY', 'user1', 'LIKE'],
    ['29-MAY', 'user20', 'COMMENT'],
    ['31-MAY', 'user11', 'COMMENT'],
    ['25-MAY', 'user145', 'LIKE'],
    ['27-MAY', 'user16', 'COMMENT'],
    ['28-MAY', 'user5', 'COMMENT'],
    ['31-MAY', 'user3', 'LIKE'],
    ['1-JUNE', 'user8', 'SAVE'],
    ['23-MAY', 'user90', 'LIKE'],
    ['1-JUNE', 'user12', 'LIKE'],
    ['1-JUNE', 'user89', 'SAVE'],
]

Hi Malik. Do you need to sort or only filter?

If sorting, what list position(s) is the sort index?
(And if you need more than one sort index, which is 1st, 2nd, etc.?)

It is really unclear what you mean in 1.

For the second task a Pythonic implementation can look like:

import collections

log_action_counts = collections.Counter(log_event[2] for log_event in logs)
log_action_counts_sorted = log_action_counts.most_common()

The first command counts the number of occurrence of individual actions.
The second command sorts the counts so that the most numerous actions are first in the resulting list.

Old implementation of the second command before I noticed collections.Counter.most_common() :slight_smile:

import operator

log_action_counts_sorted = sorted(
        log_action_counts.items(), key=operator.itemgetter(1), reverse=True)

Hello! Thank you for your response!
I will just give you what I want as a response.

  1. Imagine we have thousands of rows. and I want the logs variable to start with LIKE actions first. Like i showed in the example.
logs: List = [
    ['30-MAY', 'user1', 'LIKE'],
    ['25-MAY', 'user145', 'LIKE'],
    ['31-MAY', 'user3', 'LIKE'],
    ['23-MAY', 'user90', 'LIKE'],
    ['1-JUNE', 'user12', 'LIKE'],
    ['29-MAY', 'user20', 'COMMENT'],
    ['31-MAY', 'user11', 'COMMENT'],
    ['27-MAY', 'user16', 'COMMENT'],
    ['28-MAY', 'user5', 'COMMENT'],
    ['1-JUNE', 'user8', 'SAVE'],
    ['1-JUNE', 'user89', 'SAVE'],
]
  1. Again, imagine we have a lot of data here, and I want to see the most popular action. Please have a look at the example below. So we have 5 LIKE actions, then 4 COMMENT and 2 SAVE.
logs: List = [
    ['30-MAY', 'user1', 'LIKE'],
    ['25-MAY', 'user145', 'LIKE'],
    ['31-MAY', 'user3', 'LIKE'],
    ['23-MAY', 'user90', 'LIKE'],
    ['1-JUNE', 'user12', 'LIKE'],
    ['29-MAY', 'user20', 'COMMENT'],
    ['31-MAY', 'user11', 'COMMENT'],
    ['27-MAY', 'user16', 'COMMENT'],
    ['28-MAY', 'user5', 'COMMENT'],
    ['1-JUNE', 'user8', 'SAVE'],
    ['1-JUNE', 'user89', 'SAVE'],
]

Hope it makes more sense now, if not, please leave a comment and I will reply.

Hello!
Here I explained more: Lists / Sorting a List - #4 by malik

So, you would like to sort the parent logList items by most popular. Is “most popular” defined by how many times an action type was logged? …Or is “most popular” a static sort order of the 'LIKE' / 'COMMENT' / 'SAVE' values?

Assuming the sort order is static (because we can add dynamic sort order later) my first thought is to…

  1. filter out each action type into temporary lists
  2. concatenate those three lists together in the order you need.

[Edit: ] Your parent logList has an extra comma at the end. The Python interpreter is cleaning this up for you. If the logList is being automatically populated, the extra comma does not cause an error. (Note that the interpreter could be changed in the future so that this extra comma becomes a problem.)

[EDIT: ] Here’s a loop to create the filtered lists:

List = [ ... ]  # <<< This is your actual 'List' object, obviously,
item = ""       #     with its values and child list objects.
likeList = []
for item in List:
    if item[2] == 'LIKE':
        print(item[2])
        likeList.append(item)
print(likeList)

Sorting with priority can be done with taking advantage of key function, closure and how Python sorts tuples:

logs = [['30-MAY', 'user1', 'LIKE'],
        ['29-MAY', 'user20', 'COMMENT'],
        ['31-MAY', 'user11', 'COMMENT'],
        ['25-MAY', 'user145', 'LIKE'],
        ['27-MAY', 'user16', 'COMMENT'],
        ['28-MAY', 'user5', 'COMMENT'],
        ['31-MAY', 'user3', 'LIKE'],
        ['1-JUNE', 'user8', 'SAVE'],
        ['23-MAY', 'user90', 'LIKE'],
        ['1-JUNE', 'user12', 'LIKE'],
        ['1-JUNE', 'user89', 'SAVE'],]

def sort_with_priority(data, priority):
    def helper(record):
        if record[-1] == priority:
            return (0, record)
        return (1, record)
    return sorted(data, key=helper) # or data.sort(key=helper) for inplace sorting

print(*sort_with_priority(logs, 'LIKE'), sep="\n")

['1-JUNE', 'user12', 'LIKE']
['23-MAY', 'user90', 'LIKE']
['25-MAY', 'user145', 'LIKE']
['30-MAY', 'user1', 'LIKE']
['31-MAY', 'user3', 'LIKE']
['1-JUNE', 'user8', 'SAVE']
['1-JUNE', 'user89', 'SAVE']
['27-MAY', 'user16', 'COMMENT']
['28-MAY', 'user5', 'COMMENT']
['29-MAY', 'user20', 'COMMENT']
['31-MAY', 'user11', 'COMMENT']
2 Likes

You need a key function. Try this to sort LIKE first, COMMENT second and
SAVE third, with anything else last.

def sort_like_first(item):
    return ({'LIKE': 1, 'COMMENT': 2, 'SAVE': 3}.get(item[2], 9999), item)

List.sort(key=sort_like_first)

That is equivalent to the following code, but more efficient and faster:


new_list = []
for item in List:
    rating = item[2]
    if rating == 'LIKE':
        score = 1
    elif rating == 'COMMENT':
        score = 2
    elif rating == 'SAVE':
        score = 3
    else:
        score = 9999
    key = (score, item)
    new_list.append(key)

new_list.sort()

i = 0
for score, item in new_list:
    List[i] = item
    i += 1

Warning: I haven’t tested either code snippet, but they should work.

2 Likes

Your version groups all the “LIKE” items together, but the “COMMENT” and “SAVE” items will appear mixed together.

Thank you @aivarpaalberg This worked perfectly fine and this is what I was looking for.

Malik, here is a very compact version that does not require an external function definition.

sortidx_dict = {'LIKE': 1, 'COMMENT': 2, 'SAVE': 3}
List.sort(key=lambda idx: sortidx_dict.get(idx[2], 9999))

This is @steven.daprano’s def sort_like_first() function written as an in-line lambda: function. I find this to be more pythonic because the code is all in one location and the sort index is separated for ease of updating or manipulating through other code, which satisfies part two of your question:

By adding one more line to import Counter from the standard library, you can dynamically count the number of actions and sort by them. The following code will count any and all action types in List.

from collections import Counter
sortidx_dict = Counter([item[2] for item in List])
List.sort(key=lambda idx: sortidx_dict.get(idx[2]), reverse=True)

Pythonic Bonus: Since we’re feeding .get() with 100% valid actions, I was able to remove the ‘9999’ index value returned as a “not found” result.

If someone would like to do a speed test of the three List Key methods in this thread, I’d be very interested to know how the lambda: compares to the others.

Malik, @aivarpaalberg’s proposed code will only sort by a single value, ‘Like’. The output only looks okay because COMMENT and SAVE in your sample data are already in order by popularity.

It looks like you need to use @steven.daprano’s code or the tighter version of it that I created. The code I posted immediately above is a complete and very short & clean solution.

Test the code you decide to use with the following data to see if it sorts properly.

List = [
        ['30-MAY', 'user1', 'LIKE'],
        ['29-MAY', 'user20', 'COMMENT'],
        ['31-MAY', 'user11', 'SAVE'],
        ['25-MAY', 'user145', 'SAVE'],
        ['27-MAY', 'user16', 'COMMENT'],
        ['28-MAY', 'user5', 'LIKE'],
        ['31-MAY', 'user3', 'LIKE'],
        ['1-JUNE', 'user8', 'LIKE'],
        ['23-MAY', 'user90', 'LIKE'],
        ['1-JUNE', 'user12', 'COMMENT'],
        ['1-JUNE', 'user89', 'SAVE'],
       ]
1 Like

This is not correct to state that it will sort by single value ‘LIKE’. It does sort all the values but gives priority to ‘LIKE’. Sorting of lists is performed by default Python rules - first item in list is compared, if they are equal then second and so on.

If the objective is to sort lists by last item and give priority to ‘LIKE’ then minor adjustment of code could deliver it:

def sort_with_priority(data, priority):
    def helper(record):
        if (key := record[-1]) == priority:
            return (0, key)
        return (1, key)
    return sorted(data, key=helper) # or data.sort(key=helper) for inplace sorting

This puts ‘LIKE’ first and then sorts (groups) by last item in alphabetical order. This may or may not be what is wanted.

1 Like

It seems very likely that @malik wants to sort the entire list by frequency of each ‘action’ value.

Maybe I should have said “Aivar’s code only groups the 'LIKE' entries.” I was only pointing out that the 'COMMENT' and 'SAVE' entries are unchanged and only the 'LIKE' entries are moved to the top.

This is what I got when running the code from your post on completely randomized log entries. Do you get the same?
Screenshot_20220605-145211_Pydroid 3

(Apologies for the screenshot; I am on the mobile app and both of my Android Python interpreters do not allow output text copying.)

1 Like