Home > database >  Distribute list of dictionaries by key evenly
Distribute list of dictionaries by key evenly

Time:11-02

I have a list of dictionaries like this:

[{'User ID': '111',
  'Full Name': 'name a',
  'Role: Name': 'role a'},
 {'User ID': '222',
  'Full Name': 'name b',
  'Role: Name': 'role a'},
 {'User ID': '232',
  'Full Name': 'name c',
  'Role: Name': 'role b'},
 {'User ID': '223',
  'Full Name': 'name d',
  'Role: Name': 'role d'},
 {'User ID': '444',
  'Full Name': 'name e',
  'Role: Name': 'role d'}]

I have used the answer to this question to sort the roles evenly in a list. But I also need the their 'Full Name' as well.

So basically I need to sort the dictionary evenly by role, is this possible with the linked answer?

I don't want the dictionary roles ordered like [a, a, a, b, c, c], I want it like [a,b,a,c,a,c] or something like this anyway. Similar to how the linked question would do it.

CodePudding user response:

Use the code as below:

import pprint
import random
from functools import partial
from operator import itemgetter


data = [{'User ID': '111',
         'Full Name': 'name a',
         'Role: Name': 'role a'},
        {'User ID': '222',
         'Full Name': 'name b',
         'Role: Name': 'role a'},
        {'User ID': '232',
         'Full Name': 'name c',
         'Role: Name': 'role b'},
        {'User ID': '223',
         'Full Name': 'name d',
         'Role: Name': 'role d'},
        {'User ID': '444',
         'Full Name': 'name e',
         'Role: Name': 'role d'}]

def optimize(items, quality_function, stop=1000):
    no_improvement = 0
    best = 0
    while no_improvement < stop:
        i = random.randint(0, len(items) - 1)
        j = random.randint(0, len(items) - 1)
        copy = items[::]
        copy[i], copy[j] = copy[j], copy[i]
        q = quality_function(copy)
        if q > best:
            items, best = copy, q
            no_improvement = 0
        else:
            no_improvement  = 1
    return items


def quality_maxmindist(items, key=None):
    if key is None:
        def identity(e): return e

        key = identity

    s = 0
    for k, item in { key(item) : item for item in items}.items():
        indcs = [i for i in range(len(items)) if key(items[i]) == k]
        if len(indcs) > 1:
            s  = sum(1. / (indcs[i   1] - indcs[i]) for i in range(len(indcs) - 1))
    return 1. / s


quality_fun = partial(quality_maxmindist, key=itemgetter("Role: Name"))


res = optimize(data, quality_fun)
pprint.pprint(res)

Output

[{'Full Name': 'name a', 'Role: Name': 'role a', 'User ID': '111'},
 {'Full Name': 'name d', 'Role: Name': 'role d', 'User ID': '223'},
 {'Full Name': 'name c', 'Role: Name': 'role b', 'User ID': '232'},
 {'Full Name': 'name b', 'Role: Name': 'role a', 'User ID': '222'},
 {'Full Name': 'name e', 'Role: Name': 'role d', 'User ID': '444'}]

Basically, you add a key parameter to the function quality_maxmindist (from here), this key parameter is going to be use to determine how to items are equal.

In the particular case of your question, you could use operator.itemgetter on "Role: Name", this way items with the same role will be considered equal. See below the code changes with comments:

def quality_maxmindist(items, key=None):
    if key is None:
        def identity(e): return e

        key = identity

    s = 0
    # notice that you need to use a dictionary for finding unique items by key
    for k, item in { key(item) : item for item in items}.items():
        indcs = [i for i in range(len(items)) if key(items[i]) == k]  # notice
        if len(indcs) > 1:
            s  = sum(1. / (indcs[i   1] - indcs[i]) for i in range(len(indcs) - 1))
    return 1. / s


quality_fun = partial(quality_maxmindist, key=itemgetter("Role: Name"))
res = optimize(data, quality_fun)
  • Related