Skip to content
← All posts

Display Table of Food Orders in a Restaurant: Organizing with Hash Maps

5 min read
leetcodeproblemmediumarrayshash-mapstrings

Display Table of Food Orders in a Restaurant is LeetCode problem 1418, a medium-difficulty exercise in data aggregation. It combines hash map counting with sorting, and it is a practical example of the kind of data reshaping you do all the time in real codebases.

The problem

You are given a list of orders, where each order is a list of three strings: [customerName, tableNumber, foodItem]. Your job is to produce a "display table" for the restaurant:

  • The first row is a header: ["Table", food1, food2, ...] where the food items are sorted alphabetically.
  • Each subsequent row represents one table. It starts with the table number, followed by the count of each food item ordered at that table. Tables are sorted by table number (numerically, not lexicographically).
  • If a food item was never ordered at a particular table, its count is "0".

All values in the output are strings.

ordersDavidTable 3CevicheCorinaTable 10BeefDavidTable 3Fried ChickenCarlaTable 5WaterCarlaTable 5CevicheRousTable 3Cevicheaggregate by table + foodTableBeefCevicheFried ChickenWater3021050101101000
Six orders are aggregated into a display table. Columns are food items sorted alphabetically, rows are table numbers sorted numerically.

The challenge is not about a clever algorithm. It is about organizing data cleanly: collecting unique foods, grouping counts by table, and assembling the sorted result.

The approach: nested hash maps

The natural structure here is a hash map of hash maps. The outer map keys on table number, and the inner map keys on food item, storing the count. As you process each order, you increment the count for that table-food pair. You also maintain a set of all food items you have seen.

Think of this as building a pivot table. The rows are tables, the columns are food items, and the values are counts. The hash map of hash maps is your intermediate pivot structure before you flatten it into the final 2D list.

Step-by-step walkthrough

Step 1: Process order ["David", "3", "Ceviche"]

Table 3 does not exist in the map yet. Create an entry for it and record one Ceviche.

table_map so far:

3:{Ceviche: 1}

foods collected:

{Ceviche}

First order creates the first table entry and adds Ceviche to the food set.

Step 2: Process order ["Corina", "10", "Beef"]

Table 10 is new. Create its entry and record one Beef.

table_map so far:

3:{Ceviche: 1}
10:{Beef: 1}

foods collected:

{Ceviche, Beef}

A second table appears. The food set now has two items.

Step 3: Process order ["David", "3", "Fried Chicken"]

Table 3 already exists. Add Fried Chicken to its food counts.

table_map so far:

3:{Ceviche: 1, Fried Chicken: 1}
10:{Beef: 1}

foods collected:

{Ceviche, Beef, Fried Chicken}

Table 3 now has two different food items. Fried Chicken joins the food set.

Step 4: Process order ["Carla", "5", "Water"]

Table 5 is new. Create its entry and record one Water.

table_map so far:

3:{Ceviche: 1, Fried Chicken: 1}
5:{Water: 1}
10:{Beef: 1}

foods collected:

{Ceviche, Beef, Fried Chicken, Water}

Three tables now. Water is the fourth unique food item.

Step 5: Process order ["Carla", "5", "Ceviche"]

Table 5 exists. Add Ceviche to its food counts.

table_map so far:

3:{Ceviche: 1, Fried Chicken: 1}
5:{Water: 1, Ceviche: 1}
10:{Beef: 1}

foods collected:

{Ceviche, Beef, Fried Chicken, Water}

Ceviche already exists in the food set, so no new food is added.

Step 6: Process order ["Rous", "3", "Ceviche"]

Table 3 exists and already has Ceviche. Increment its count from 1 to 2.

table_map so far:

3:{Ceviche: 2, Fried Chicken: 1}
5:{Water: 1, Ceviche: 1}
10:{Beef: 1}

foods collected:

{Ceviche, Beef, Fried Chicken, Water}

The count for Ceviche at table 3 goes up. All orders are now processed.

Final: Sort and build the output table

Sort food names alphabetically: [Beef, Ceviche, Fried Chicken, Water]. Sort table numbers numerically: [3, 5, 10]. Fill in counts (0 where no order exists).

table_map so far:

3:{Beef: 0, Ceviche: 2, Fried Chicken: 1, Water: 0}
5:{Beef: 0, Ceviche: 1, Fried Chicken: 0, Water: 1}
10:{Beef: 1, Ceviche: 0, Fried Chicken: 0, Water: 0}

foods collected:

{Beef, Ceviche, Fried Chicken, Water}

The header row is ["Table", "Beef", "Ceviche", "Fried Chicken", "Water"]. Each data row starts with the table number, followed by counts in food-column order.

The code

from collections import defaultdict

def displayTable(orders):
    table_map = defaultdict(lambda: defaultdict(int))
    foods = set()

    for customer, table, food in orders:
        table_map[table][food] += 1
        foods.add(food)

    sorted_foods = sorted(foods)
    sorted_tables = sorted(table_map.keys(), key=int)

    header = ["Table"] + sorted_foods
    result = [header]

    for table in sorted_tables:
        row = [table]
        for food in sorted_foods:
            row.append(str(table_map[table][food]))
        result.append(row)

    return result

Here is what each section does:

  • table_map = defaultdict(lambda: defaultdict(int)) creates a nested default dictionary. Accessing table_map["3"]["Ceviche"] auto-initializes to 0 if the key path does not exist yet.
  • The first loop processes every order. We ignore the customer name (it is not needed for the output), increment the food count for that table, and add the food to our set of unique food names.
  • sorted_foods = sorted(foods) gives us the alphabetically sorted column headers.
  • sorted_tables = sorted(table_map.keys(), key=int) sorts table numbers numerically. Since table numbers are strings, we pass key=int to sort by their integer value.
  • The final loop builds each row by looking up the count for every food at that table. Missing entries default to 0 thanks to defaultdict(int).

The customer name is completely irrelevant to the output. You never need to track who ordered what. Only the table number and food item matter. This is a common pattern in aggregation problems: some input fields are distractors.

Complexity analysis

ApproachTimeSpace
Nested hash mapO(n + t * f * log f + t * log t)O(t * f)

Here n is the number of orders, t is the number of unique tables, and f is the number of unique food items. Processing all orders takes O(n). Sorting the food names costs O(f log f), and sorting the table numbers costs O(t log t). Building the output table requires iterating through t tables times f foods. In practice, the sorting dominates only when t or f is large relative to n.

The building blocks

Hash map of hash maps (nested aggregation)

The core pattern here is nesting one hash map inside another. The outer key groups by one dimension (table number), and the inner key groups by a second dimension (food item). This two-level structure is the go-to approach whenever you need to aggregate data along two axes. You will see it in problems involving matrices, frequency tables, and graph adjacency lists.

The template is always the same:

from collections import defaultdict

agg = defaultdict(lambda: defaultdict(int))
for item in data:
    agg[group_key][sub_key] += 1

Collect-then-sort output assembly

After aggregation, you need to produce sorted output. The pattern is: collect all unique keys into a set during the aggregation pass, sort them once at the end, then iterate in sorted order to build the result. This avoids repeatedly sorting or maintaining sorted order during insertion, which would add unnecessary complexity.

Edge cases

Single table, single food. If every order is for the same table and the same item, the output has just one data row with a single count. The algorithm handles this without special logic.

Many tables, one food. If only one food item was ever ordered, the output has just two columns: "Table" and that food. Every row has either "0" or a positive count.

Same customer, multiple tables. A customer can order from different tables. Since we ignore customer names during aggregation, this causes no issues.

Table numbers with different digit lengths. Table "3" and table "10" must be sorted numerically (3 before 10), not lexicographically ("10" before "3"). Using key=int in the sort handles this correctly.

From understanding to recall

This problem tests whether you can organize messy input into a clean tabular output. The algorithm itself is not tricky. The challenge is choosing the right data structure (nested defaultdict plus a set) and remembering to sort the output along both axes.

The building block worth internalizing is the nested hash map aggregation. Once you recognize that the output is a two-dimensional pivot table, the code writes itself: one pass to fill the map, one sort for columns, one sort for rows, and a final pass to assemble the result.

Spaced repetition helps you lock in this pattern. After a few practice rounds, you will not need to think about the data structure choice. The moment you see "group by X and count Y," the nested defaultdict pattern will come to mind automatically. The only thing left to figure out is the specific keys and the output format.

Related posts