Home > Back-end >  How to make a Custom Sorting Function for Dictionary Key Values?
How to make a Custom Sorting Function for Dictionary Key Values?

Time:01-08

I have a dictionary whose key values are kind of like this,

CC-1A
CC-1B
CC-1C
CC-3A
CC-3B
CC-5A
CC-7A
CC-7B
CC-7D
SS-1A
SS-1B
SS-1C
SS-3A
SS-3B
SS-5A
SS-5B

lst = ['CC-1A', 'CC-1B', 'CC-1C', 'CC-3A', 'CC-3B', 'CC-5A', 'CC-7A', 'CC-7B', 
       'CC-7D', 'SS-1A', 'SS-1B', 'SS-1C', 'SS-3A', 'SS-3B', 'SS-5A', 'SS-5B']
d = dict.fromkeys(lst)

^Not exactly in this order, but in fact they are all randomly placed in the dictionary as key values.

Now, I want to sort them. If I use the built in function to sort the dictionary, it sorts all the key values according to the order given above.

However, I want the dictionary to be first sorted based upon the values after the - sign (i.e. 1A, 1B, 1C etc.) and then based upon the first two characters.

So, for the values given above, following would be my sorted list,

    CC-1A
    CC-1B
    CC-1C
    SS-1A
    SS-1B
    SS-1C
    CC-3A
    CC-3B
    SS-3A
    SS-3B
    CC-5A
    and so on
  1. First, sorting is done based upon the "4th" character in the keys. (that is, 1, 3, etc.)

  2. Then sorting is done based upon the last character (i.e. A, B etc.)

  3. Then sorting is done based upon the first two characters of the keys (i.e. CC, SS etc.)

Is there any way to achieve this?

CodePudding user response:

Your "wanted" and your sorting description deviate.

Your "wanted" can be achieved by

di = {"CC-1A":"value1","CC-1A":"value2","CC-1B":"value3",
"CC-1C":"value4","CC-3A":"value5","CC-3B":"value6",
"CC-5A":"value7","CC-7A":"value8","CC-7B":"value9",
"CC-7D":"value0","SS-1A":"value11","SS-1B":"value12",
"SS-1C":"value13","SS-3A":"value14","SS-3B":"value15",
"SS-5A":"value16","SS-5B":"value17"}

print(*((v,di[v]) for v in sorted(di, key= lambda x: (x[3], x[:2], x[4]) )), 
      sep="\n")

to get

('CC-1A', 'value2')
('CC-1B', 'value3')
('CC-1C', 'value4')
('SS-1A', 'value11')
('SS-1B', 'value12')
('SS-1C', 'value13')
('CC-3A', 'value5')
('CC-3B', 'value6')
('SS-3A', 'value14')
('SS-3B', 'value15')
('CC-5A', 'value7')
('SS-5A', 'value16')
('SS-5B', 'value17')
('CC-7A', 'value8')
('CC-7B', 'value9')
('CC-7D', 'value0')

which sorts by number (Pos 4 - (1based)), Start (Pos 1 2 (1based)) then letter (Pos 5 (1based))

but that conflicts with

  1. First, sorting is done based upon the "4th" character in the keys. (that is, 1, 3, etc.)

  2. Then sorting is done based upon the last character (i.e. A, B etc.)

  3. Then sorting is done based upon the first two characters of the keys (i.e. CC, SS etc.)

CodePudding user response:

One suggestion is to use a nested dictionary, so instead of:

my_dict = {'CC-1A1': 2,
           'CC-1A2': 3,
           'CC-1B': 1,
           'CC-1C': 5,
           'SS-1A': 33,
           'SS-1B': 23,
           'SS-1C': 31,
           'CC-3A': 55,
           'CC-3B': 222,
           }

you would have something like:

my_dict = {'CC': {'1A1': 2, '1A2': 3, '1B': 1, '1C': 5, '3A': 55, '3B': 222},
           'SS': {'1A': 33, '1B': 22, '1C': 31}
           }

which would allow you to sort first based on the leading number/characters and then by group. (Actually I think you want this concept reversed based on your question).

Then you can create two lists with your sorted keys/values by doing something like:

top_keys = sorted(my_dict)
keys_sorted = []
values_sorted = []

for key in top_keys:
    keys_sorted.append([f"{key}-{k}" for k in my_dict[key].keys()])
    values_sorted.append([v for v in my_dict[key].values()])

flat_keys = [key for sublist in keys_sorted for key in sublist]
flat_values = [value for sublist in values_sorted for value in sublist]

Otherwise, you'd have to implement a custom sorting algorithm based first the characters after the - and subsequently on the initial characters.

CodePudding user response:

You can write a function to build a sorting key that will make the required decomposition of the key strings and return a tuple to sort by. Then use that function as the key= parameter of the sorted function:

D = {'CC-1A': 0, 'CC-1B': 1, 'CC-1C': 2, 'CC-3A': 3, 'CC-3B': 4, 
    'CC-5A': 5, 'CC-7A': 6, 'CC-7B': 7, 'CC-7D': 8, 'SS-1A': 9, 
    'SS-1B': 10, 'SS-1C': 11, 'SS-3A': 12, 'SS-3B': 13, 'SS-5A': 14, 
    'SS-5B': 15}

def sortKey(s):
    L,R = s.split("-",1)
    return (R[:-1],L)

D={k:D[k] for k in sorted(D.keys(),key=sortKey)}

print(D)
{'CC-1A': 0, 
 'CC-1B': 1, 
 'CC-1C': 2, 
 'SS-1A': 9, 
 'SS-1B': 10, 
 'SS-1C': 11, 
 'CC-3A': 3, 
 'CC-3B': 4, 
 'SS-3A': 12, 
 'SS-3B': 13, 
 'CC-5A': 5, 
 'SS-5A': 14, 
 'SS-5B': 15, 
 'CC-7A': 6, 
 'CC-7B': 7, 
 'CC-7D': 8}

If you expect the numbers to eventually go beyond 9 and want a numerical order, then right justify the R part in the tuple: e.g. return (R[:-1].rjust(10),L)

CodePudding user response:

You could use a custom function that implements your rule as sorting key:

def get_order(tpl):
    s = tpl[0].split('-')
    return (s[1][0], s[0], s[1][1])


out = dict(sorted(d.items(), key=get_order))

Output:

{'CC-1A': None, 'CC-1B': None, 'CC-1C': None, 'SS-1A': None, 'SS-1B': None, 'SS-1C': None, 'CC-3A': None, 'CC-3B': None, 'SS-3A': None, 'SS-3B': None, 'CC-5A': None, 'SS-5A': None, 'SS-5B': None, 'CC-7A': None, 'CC-7B': None, 'CC-7D': None}
  •  Tags:  
  • Related