Problem with removing an element from SortedSet

Question:

I was solving a problem food rating system on LeetCode. The system involves managing foods, cuisines, and ratings. I’ve implemented a class FoodRatings with functions to change ratings and return the highest-rated food for a particular cuisine, considering lexicographical order in case of ties.

Here’s an excerpt of my code:

from sortedcontainers import SortedSet
from typing import List


class FoodRatings:

    def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):
        self.food_map = {}  # Food: [cuisine, rating, food]
        self.cuisines_map = {}  # Cuisine: SortedSet(Food)
        length = len(foods)

        for index in range(length):
            self.food_map[foods[index]] = [cuisines[index], ratings[index], foods[index]]

            if cuisines[index] in self.cuisines_map:
                self.cuisines_map[cuisines[index]].add(foods[index])
            else:
                # Making a SortedSet ordered on rating and lexicographical order of food
                self.cuisines_map[cuisines[index]] = SortedSet([foods[index]], key=lambda x:(-self.food_map[x][1], self.food_map[x][2]))

    def changeRating(self, food: str, newRating: int) -> None:
        cuisine = self.food_map[food][0]

        # Modifying the rating, then deleting the food from SortedSet and then adding it again
        self.food_map[food][1] = newRating
        self.cuisines_map[cuisine].discard(food)
        self.cuisines_map[cuisine].add(food)


    def highestRated(self, cuisine: str) -> str:
        return self.cuisines_map[cuisine][0]


# Code to reproduce the issue:
obj = FoodRatings(["kimchi","miso","sushi","moussaka","ramen","bulgogi"],
                  ["korean","japanese","japanese","greek","japanese","korean"],
                  [9,12,8,15,14,7])

obj.changeRating("sushi", 16)

Here when changeRating("sushi", 16) is called, I get an error stating: 'sushi' not in List but after slightly modifying the changeRating() function, the error is resolved.

    def changeRating(self, food: str, newRating: int) -> None:
        cuisine = self.food_map[food][0]
        # Deleting the food from SortedSet, then modifying the rating and then adding it again
        self.cuisines_map[cuisine].discard(food)

        self.food_map[food][1] = newRating

        self.cuisines_map[cuisine].add(food)

Why I am unable to remove item from the SortedSet in case 1 but I am able to remove it in case 2?

Asked By: Anunad Singh

||

Answers:

This happens because you should not change the key of an item that is still in the list. The documentation of SortedSet warns about this:

Sorted set values must be hashable and comparable. The hash and total ordering of values must not change while they are stored in the sorted set.

In your case, your sorted set got the following as key definition:

key=lambda x:(-self.food_map[x][1], self.food_map[x][2])

By consequence you should never alter self.food_map[x][1] nor self.food_map[x][2] when x is still in the sorted list. This will lead to unreliable results.

This explains why it works correctly when you first remove the item from the sorted list and only then update one of its key fields.

Answered By: trincot
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.