Sorting nested dictionary

Viewed 54

I have some data, which look like this:

tuplelist = [
    (datetime.date(2020, 4, 20), 4.23, 'EUR'),
    (datetime.date(2020, 4, 20), 3.76, 'USD'),
    (datetime.date(2020, 4, 20), 4.21, 'EUR'),
    (datetime.date(2020, 4, 20), 5.20, 'GPB'),
    (datetime.date(2020, 4, 20), 3.77, 'USD'),
    (datetime.date(2020, 4, 20), 4.27, 'EUR'),
    (datetime.date(2020, 4, 20), 3.79, 'USD'),
    (datetime.date(2020, 4, 20), 4.30, 'EUR'),
    (datetime.date(2020, 4, 20), 5.14, 'GPB'),
    (datetime.date(2020, 4, 20), 3.77, 'USD'),
    (datetime.date(2020, 4, 25), 4.23, 'EUR'),
    (datetime.date(2020, 4, 25), 3.76, 'USD'),
    (datetime.date(2020, 4, 25), 4.21, 'EUR'),
    (datetime.date(2020, 4, 25), 5.20, 'GPB'),
    (datetime.date(2020, 4, 25), 3.77, 'USD'),
    (datetime.date(2020, 4, 27), 4.27, 'EUR'),
    (datetime.date(2020, 4, 27), 3.79, 'USD'),
    (datetime.date(2020, 4, 27), 4.30, 'EUR'),
    (datetime.date(2020, 4, 27), 5.14, 'GPB'),
    (datetime.date(2020, 4, 28), 3.77, 'USD'),
    (datetime.date(2020, 4, 28), 4.23, 'EUR'),
    (datetime.date(2020, 5, 2), 3.76, 'USD'),
    (datetime.date(2020, 5, 2), 4.21, 'EUR'),
    (datetime.date(2020, 5, 2), 5.20, 'GPB'),
    (datetime.date(2020, 5, 2), 3.77, 'USD'),
    (datetime.date(2020, 5, 2), 4.27, 'EUR'),
    (datetime.date(2020, 5, 5), 3.79, 'USD'),
    (datetime.date(2020, 5, 5), 4.30, 'EUR'),
    (datetime.date(2020, 5, 5), 5.14, 'GPB'),
    (datetime.date(2020, 5, 5), 3.77, 'USD'),
    (datetime.date(2020, 4, 20), 4.23, 'EUR'),
    (datetime.date(2020, 4, 20), 3.76, 'USD'),
    (datetime.date(2020, 4, 20), 4.21, 'EUR'),
    (datetime.date(2020, 4, 20), 5.20, 'GPB'),
    (datetime.date(2020, 4, 20), 3.77, 'USD'),
    (datetime.date(2020, 4, 20), 4.27, 'EUR'),
    (datetime.date(2020, 4, 20), 3.79, 'USD'),
    (datetime.date(2020, 4, 20), 4.30, 'EUR'),
    (datetime.date(2020, 4, 20), 5.14, 'GPB'),
    (datetime.date(2020, 4, 20), 3.77, 'USD'),
    (datetime.date(2020, 4, 25), 4.23, 'EUR'),
    (datetime.date(2020, 4, 25), 3.76, 'USD'),
    (datetime.date(2020, 4, 25), 4.21, 'EUR'),
    (datetime.date(2020, 4, 25), 5.20, 'GPB'),
    (datetime.date(2020, 4, 25), 3.77, 'USD'),
    (datetime.date(2020, 4, 27), 4.27, 'EUR'),
    (datetime.date(2020, 4, 27), 3.79, 'USD'),
    (datetime.date(2020, 4, 27), 4.30, 'EUR'),
    (datetime.date(2020, 4, 27), 5.14, 'GPB'),
    (datetime.date(2020, 4, 28), 3.77, 'USD'),
    (datetime.date(2020, 4, 28), 4.23, 'EUR'),
    (datetime.date(2020, 5, 2), 3.76, 'USD'),
    (datetime.date(2020, 5, 2), 4.21, 'EUR'),
    (datetime.date(2020, 5, 2), 5.20, 'GPB'),
    (datetime.date(2020, 5, 2), 3.77, 'USD'),
    (datetime.date(2020, 5, 2), 4.27, 'EUR'),
    (datetime.date(2020, 5, 5), 3.79, 'USD'),
    (datetime.date(2020, 5, 5), 4.30, 'EUR'),
    (datetime.date(2020, 5, 5), 5.14, 'GPB'),
    (datetime.date(2020, 5, 5), 3.77, 'USD')
]

I managed to group it by date an currency and sum the values of each curriency. Now, I'd like to sort the currency values in descending manner. However I got some problems with two values (the same problem is during sorting in ascending way, but then this values are at the end): Output

Do you have any clue if my sorting loop is correct? Moreover, it is possible not to change the dictionary to list, while sorting?

Here is my code:

# grouping elements by date and currency
result = {}
for key, values in groupby(tuplelist, lambda x: x[0]):
    for _, v1, v2 in values:
        result.setdefault(key, {}).setdefault(v2, []).append(v1)

# summing value of each currency of each day
result2 = {}
for key, values in result.items():
    for k, val in values.items():
        total = sum(val)
        result2.setdefault(key, {}).setdefault(k, "%.2f" % total)

print("-----------------------BEFORE SORTING------------------------------------")
for items in result2.items():
    print(items)

# sorting the values of currencies in descending order
sorted_dict = {}
for key, values in result2.items():
    sorted_values = sorted(values.items(), key=lambda item: item[1], reverse=True)
    sorted_dict[key] = sorted_values

print("-----------------------SORTED VALUES------------------------------------")
for items in sorted_dict.items():
    print(items)
2 Answers
# grouping elements by date and currency
result = {}
for key, values in groupby(tuplelist, lambda x: x[0]):
    for _, v1, v2 in values:
        result.setdefault(key, {}).setdefault(v2, []).append(v1)

# summing value of each currency of each day
result2 = {}
for key, values in result.items():
    for k, val in values.items():
        total = sum(val)
        result2.setdefault(key, {}).setdefault(k, round(total,2))

print("-----------------------BEFORE SORTING------------------------------------")
for items in result2.items():
    print(items)

# sorting the values of currencies in descending order
sorted_dict = {}
for key, values in result2.items():
    sorted_values = sorted(values.items(), key=lambda item: item[1], reverse=True)
    sorted_dict[key] = sorted_values

print("-----------------------SORTED VALUES------------------------------------")
for items in sorted_dict.items():
    print(items)

I make a change at result2 where i change <"%.2f" % total> to <round(total,2)> as by using <"%.2f" % total> will turn your value to string, therefore unable to sort as per your requirement.

As Shlim mentioned in his answer, the sorting in your current output is correct if you were sorting by string values. In order to sort them by their numeric values you can make the adjustments Shlim suggests.

My suggestion is instead of having so many loops and creating so many external data structures, why not just make it one loop and one external dictionary? For example:

result = {}
for key, values in groupby(tuplelist, lambda x: x[0]):
    for _, v1, v2 in values:
        if key in result and v2 in result[key]:
            result[key][v2] += v1
        elif key not in result:
            result[key] = {v2: v1}
        else:
            result[key][v2] = v1
    sorted_values = sorted(
        result[key].items(), key=lambda item: item[1], reverse=True
    )
    rounded = map(lambda x: (x[0], round(x[1],2)), sorted_values)
    result[key].update(list(rounded))

To answer your other question, you cannot sort a dictionary directly with any of the builtin sorting functions. This is because they all rely on integer indexes which dictionaries do not have.

Related