Integer IDs
Unique Identifiers
ID Generation
Number Sets
Integer Mapping

Generating ids for a set of integers

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

When people ask about generating IDs for a set of integers, they usually mean one of two things: assign a stable compact ID to each distinct integer, or generate new unique integers for future values. Those are different problems and need different designs. This article focuses on the common mapping problem first, then covers when hashing, sequential counters, or UUID-style solutions make sense instead.

Compact IDs for an Existing Set

If you already have a set of integers and want each unique value mapped to a dense ID range such as 0..n-1, a dictionary is the simplest solution.

Example in Python:

python
1values = [50, 10, 50, 99, 10, 42]
2
3id_map = {}
4next_id = 0
5
6for value in values:
7    if value not in id_map:
8        id_map[value] = next_id
9        next_id += 1
10
11print(id_map)

This preserves first-seen order. The first distinct integer gets ID 0, the next distinct integer gets 1, and so on.

Sorted vs Insertion Order Mapping

Sometimes IDs should depend on numeric order rather than encounter order. In that case, deduplicate and sort first.

python
1values = [50, 10, 50, 99, 10, 42]
2
3sorted_unique = sorted(set(values))
4id_map = {value: idx for idx, value in enumerate(sorted_unique)}
5
6print(sorted_unique)  # [10, 42, 50, 99]
7print(id_map)         # {10: 0, 42: 1, 50: 2, 99: 3}

Choose the rule deliberately:

  • insertion order gives stable IDs relative to data arrival
  • sorted order gives stable IDs relative to numeric rank

Neither is universally better.

A Reusable Java Example

If your application is in Java, a LinkedHashMap is a good fit when you want stable first-seen IDs.

java
1import java.util.Arrays;
2import java.util.LinkedHashMap;
3import java.util.Map;
4
5public class IntegerIdGenerator {
6    public static Map<Integer, Integer> assignIds(int[] values) {
7        Map<Integer, Integer> ids = new LinkedHashMap<>();
8        int nextId = 0;
9
10        for (int value : values) {
11            if (!ids.containsKey(value)) {
12                ids.put(value, nextId++);
13            }
14        }
15
16        return ids;
17    }
18
19    public static void main(String[] args) {
20        int[] values = {50, 10, 50, 99, 10, 42};
21        System.out.println(assignIds(values));
22    }
23}

This is a clean approach when the mapping must be reproducible within one run or persisted externally.

When You Need IDs for Future Inserts

If new integers will keep arriving over time, the system needs a persistent mapping store, not just an in-memory map. Otherwise the same integer may receive a different ID on the next process start.

Typical options:

  • keep the map in a database table
  • serialize it to disk
  • rebuild it from authoritative source data on startup

If the mapping is business-critical, persistence rules matter more than the choice of programming language.

Avoid Hashes Unless You Accept Collisions or Large Values

A hash function can produce a derived number from each integer, but that is not the same as a compact unique ID. Hashes can collide, and even good hashes do not guarantee a dense range.

For example, this is deterministic but not a compact ID assignment:

python
values = [10, 42, 99]
hash_ids = {value: hash(value) for value in values}
print(hash_ids)

Use hashing only if you truly need hashing behavior, not when you need small stable identifiers.

Direct Mapping Can Already Be the Best ID

If the original integers are already unique, bounded, and acceptable for downstream systems, you may not need a second ID at all. Extra indirection adds complexity.

Examples where original integers may already be enough:

  • database primary keys
  • fixed numeric codes from an external standard
  • compact ranges that users never see directly

Before building an ID layer, ask what problem it actually solves.

Reverse Lookup and Compression

Many systems need both directions:

  • integer to assigned ID
  • assigned ID back to original integer

That is easy to support with a list or reverse map.

python
1values = [50, 10, 50, 99, 10, 42]
2id_map = {}
3reverse = []
4
5for value in values:
6    if value not in id_map:
7        id_map[value] = len(reverse)
8        reverse.append(value)
9
10print(id_map[42])   # assigned ID
11print(reverse[2])   # original integer for ID 2

This is especially useful for matrix indexing, graph compression, and feature encoding.

Performance Considerations

For most workloads, dictionary-based assignment is O(n) average time. Sorting first changes the cost to roughly O(n log n) but gives deterministic numeric ordering.

Performance questions usually matter only when:

  • the dataset is very large
  • IDs are generated repeatedly
  • the mapping must be shared across services

In those cases, persistence strategy and memory usage become part of the design.

Common Pitfalls

  • Using a hash when the real need is a compact one-to-one mapping.
  • Forgetting to persist the mapping when IDs must remain stable across runs.
  • Mixing insertion-order IDs and sorted-order IDs in different parts of the system.
  • Generating a new ID every time an integer is seen instead of reusing the existing mapping.
  • Adding a new ID layer even though the original integers were already suitable identifiers.

Summary

  • For an existing integer set, a map from integer to sequential ID is usually the right solution.
  • Decide whether IDs should follow first-seen order or sorted numeric order.
  • Persist the mapping if future runs must generate the same IDs.
  • Use hashes only when you really need hashing semantics, not compact identifiers.
  • If the original integers already work as identifiers, do not add unnecessary mapping complexity.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.