Design a Food Rating System

medium hash map design heap ordered set

Problem

Support changeRating(food, newRating) and highestRated(cuisine). For ties, return the lexicographically smallest food. Use a hash map plus a per-cuisine sorted set keyed by (−rating, food).

Inputinit: [(kimchi, korean, 9), (miso, japanese, 12), (sushi, japanese, 8)]
OutputhighestRated("japanese") → "miso"
After changeRating("sushi", 16), highestRated("japanese") becomes "sushi".

from sortedcontainers import SortedList

class FoodRatings:
    def __init__(self, foods, cuisines, ratings):
        self.food = {}
        self.by_cuisine = {}
        for f, c, r in zip(foods, cuisines, ratings):
            self.food[f] = (r, c)
            self.by_cuisine.setdefault(c, SortedList()).add((-r, f))

    def changeRating(self, food, newRating):
        r, c = self.food[food]
        self.by_cuisine[c].remove((-r, food))
        self.by_cuisine[c].add((-newRating, food))
        self.food[food] = (newRating, c)

    def highestRated(self, cuisine):
        return self.by_cuisine[cuisine][0][1]
class FoodRatings {
  constructor(foods, cuisines, ratings) {
    this.food = new Map();
    this.byCuisine = new Map();
    for (let i = 0; i < foods.length; i++) {
      this.food.set(foods[i], { r: ratings[i], c: cuisines[i] });
      if (!this.byCuisine.has(cuisines[i])) this.byCuisine.set(cuisines[i], []);
      this.byCuisine.get(cuisines[i]).push(foods[i]);
    }
  }
  changeRating(food, newRating) {
    const e = this.food.get(food);
    e.r = newRating;
  }
  highestRated(cuisine) {
    const list = this.byCuisine.get(cuisine);
    let best = list[0];
    for (const f of list) {
      const a = this.food.get(f), b = this.food.get(best);
      if (a.r > b.r || (a.r === b.r && f < best)) best = f;
    }
    return best;
  }
}
class FoodRatings {
    Map<String, int[]> food = new HashMap<>();
    Map<String, String> foodCuisine = new HashMap<>();
    Map<String, TreeSet<String>> byCuisine = new HashMap<>();

    public FoodRatings(String[] foods, String[] cuisines, int[] ratings) {
        for (int i = 0; i < foods.length; i++) {
            food.put(foods[i], new int[]{ ratings[i] });
            foodCuisine.put(foods[i], cuisines[i]);
            byCuisine.computeIfAbsent(cuisines[i], k -> new TreeSet<>(
                (a, b) -> {
                    int ra = food.get(a)[0], rb = food.get(b)[0];
                    if (ra != rb) return rb - ra;
                    return a.compareTo(b);
                }
            )).add(foods[i]);
        }
    }
    public void changeRating(String foodName, int newRating) {
        TreeSet<String> set = byCuisine.get(foodCuisine.get(foodName));
        set.remove(foodName);
        food.get(foodName)[0] = newRating;
        set.add(foodName);
    }
    public String highestRated(String cuisine) {
        return byCuisine.get(cuisine).first();
    }
}
class FoodRatings {
    unordered_map<string, pair<int, string>> food;
    unordered_map<string, set<pair<int, string>>> byCuisine;
public:
    FoodRatings(vector<string>& foods, vector<string>& cuisines, vector<int>& ratings) {
        for (int i = 0; i < (int)foods.size(); i++) {
            food[foods[i]] = { ratings[i], cuisines[i] };
            byCuisine[cuisines[i]].insert({ -ratings[i], foods[i] });
        }
    }
    void changeRating(string foodName, int newRating) {
        auto& e = food[foodName];
        byCuisine[e.second].erase({ -e.first, foodName });
        e.first = newRating;
        byCuisine[e.second].insert({ -newRating, foodName });
    }
    string highestRated(string cuisine) {
        return byCuisine[cuisine].begin()->second;
    }
};
Time: O(log n) per op Space: O(n)