JavaScript Coder

Harness the Power of TypeScript Dictionaries: An In-Depth Tutorial

dictionary type in typescript typescript dictionary type

In programming, a dictionary is like a real-life dictionary, where you look up a ‘word’ (key) to find its ‘meaning’ (value). TypeScript, like JavaScript, doesn’t have a built-in Dictionary type, but we can use an object to achieve the same functionality. Why do we use it? Simply because it’s a very efficient way to store and retrieve data where the order is not important, but the link between keys and values is.

Defining a Dictionary in TypeScript

To define a dictionary in TypeScript, we use an object. The type notation is:
{ [key: type]: valueType }.

Here’s a simple example:

let dictionary: { [key: string]: number } = {};

In this code, we have a dictionary where the keys are strings and the values are numbers.

TypeScript Dictionary Operations

With the dictionary defined, let’s see how we can work with it:

Adding Key-Value Pairs

We can add new key-value pairs like this:

dictionary['apple'] = 5;
dictionary['banana'] = 10;

Accessing Dictionary Values

We can access the values by their keys:

console.log(dictionary['apple']); // Output: 5
console.log(dictionary['banana']); // Output: 10

Removing Key-Value Pairs

We can remove key-value pairs using the delete keyword:

delete dictionary['apple'];

Modifying Values

We can modify a value by assigning a new value to a key:

dictionary['banana'] = 20;

Checking if a Key Exists

We can check if a key exists using the in keyword:

if ('apple' in dictionary) {
  console.log('apple exists');
} else {
  console.log('apple does not exist');
}

Type Safety with Dictionary Type

One of the main benefits of TypeScript is its type safety. This means that TypeScript will ensure that we only use our dictionary with the types we defined (string keys and number values in our example). So, if we tried to set a key or a value to a wrong type, TypeScript would show an error:

dictionary['orange'] = 'a lot'; // Error: Type 'string' is not assignable to type 'number'.

Advanced Dictionary Types

As we get more comfortable with TypeScript dictionaries, we can explore advanced features:

Dictionaries with Enum Keys

In TypeScript, we can use an enum as a key in our dictionary. This can provide additional type safety and code clarity:

enum Fruit {
  Apple,
  Banana,
  Orange
}

let fruitBowl: { [K in Fruit]?: number } = {};
fruitBowl[Fruit.Apple] = 5;
fruitBowl[Fruit.Banana] = 10;

Readonly Dictionaries

TypeScript allows us to define a dictionary as readonly, which means that once we define it, we can’t change it:

const readonlyDictionary: Readonly<{ [key: string]: number }> = {
  apple: 5,
  banana: 10
};

readonlyDictionary['apple'] = 10; // Error: Index signature in type 'Readonly<{ [key: string]: number; }>' only permits reading.

Nested Dictionaries

TypeScript also supports dictionaries within dictionaries. These can be useful for more complex data structures:

let nestedDictionary: { [key: string]: { [key: string]: number } } = {
  fruit: {
    apple: 5,
    banana: 10
  },
  vegetable: {
    carrot: 20,
    potato: 25
  }
};

Common Use Cases of TypeScript Dictionaries

Dictionaries are used in a lot of different scenarios. For example:

Storing Data for Fast Lookup

If you want to keep track of the count of each word in a list of words, a dictionary would be a perfect choice. Keys can be the words, and values can be the counts.

Grouping by a Key

If you have a list of objects and you want to group them by a certain property, you can use a dictionary. The property value can be the key, and the value can be an array of objects that have that property value.

Counting Occurrences of Elements

Dictionaries are also excellent for counting the occurrences of elements in a list.

Let’s see some interesting uses of dictionaries to solve some interesting problems:

Usage example: Word Frequency Counter

This is a very straightforward yet effective use of dictionaries. Given a list or a stream of words, we can use a dictionary to keep track of the frequency of each word. The word itself is the key and the frequency count is the value. Each time a word appears, we increment its count in the dictionary. This forms the basis of many text analysis algorithms. Absolutely, here’s an example of how you could solve the Word Frequency Count problem using a TypeScript dictionary:

function wordFrequencyCount(text: string): { [word: string]: number } {
    // Create an empty dictionary
    let frequencyDictionary: { [word: string]: number } = {};

    // Convert text to lower case and split it into words
    let words = text.toLowerCase().split(/\s+/);

    // Iterate over each word
    for (let word of words) {
        // If word exists in dictionary, increment its count, else set its count to 1
        if (word in frequencyDictionary) {
            frequencyDictionary[word]++;
        } else {
            frequencyDictionary[word] = 1;
        }
    }

    // Return the frequency dictionary
    return frequencyDictionary;
}

let text = "Hello world! This is a test. Hello again. Is this a test?";

let frequencies = wordFrequencyCount(text);

console.log(frequencies);

In this code, we first create an empty dictionary frequencyDictionary. We then split the text into words, iterating over each one. If a word is already a key in our dictionary, we increment its value; if not, we add it to the dictionary with a value of 1.

Finally, we return our dictionary. If you run this code with the example string "Hello world! This is a test. Hello again. Is this a test?", the output will be a dictionary with the frequency of each word in the text.

Please note that this is a basic version of word frequency count, for simplicity it just considers space for word splitting. In a more robust solution, you would want to consider punctuation and common word filtering (like removing ‘a’, ‘is’, ’the’ etc). Also this doesn’t differentiate words like ’test’ and ’test?’ because of the punctuation.

Usage example: Subarray Sum Equals K

Given an array of integers and an integer K, you need to find the total number of continuous subarrays whose sum equals to K. This problem can be solved by using a dictionary to store the cumulative sum of the elements and the number of times each sum occurs. If a cumulative sum up to index j is equal to sum, we know that there are exactly sum occurrences of that sum, and we add this to our total count.

function subarraySum(nums: number[], k: number): number {
    let count = 0;
    let sum = 0;
    let sumFrequency: { [sum: number]: number } = { 0: 1 };

    for (let i = 0; i < nums.length; i++) {
        sum += nums[i];
        if ((sum - k) in sumFrequency) {
            count += sumFrequency[sum - k];
        }
        if (sum in sumFrequency) {
            sumFrequency[sum]++;
        } else {
            sumFrequency[sum] = 1;
        }
    }

    return count;
}

let nums = [1, 1, 1];
let k = 2;

console.log(subarraySum(nums, k)); // Output: 2

In this function, sumFrequency is a dictionary that stores the cumulative sum of the array elements up to the current index as keys and the number of times each sum occurs as values.

The sum variable keeps track of the cumulative sum of the array elements. If the difference sum - k exists in the dictionary, it means that there are one or more subarrays ending at the current index that sum to k, so we increase count by the frequency of sum - k in the dictionary.

Then, we update the frequency of the current sum in the dictionary. If sum is already a key in the dictionary, we increment its value, otherwise, we set its value to 1.

Finally, we return count, which is the total number of continuous subarrays whose sum equals to k.

In this example, the input array is [1, 1, 1] and k is 2. The output is 2 because there are two subarrays [1, 1] and [1, 1] (starting from the second index) that sum to 2.

Common Pitfalls and How to Avoid Them

While dictionaries are very powerful, there are some pitfalls to be aware of:

Handling Undefined Values

If you try to access a value using a key that doesn’t exist in the dictionary, you’ll get undefined. Always check if a key exists before using its value.

Iterating Over a Dictionary

To iterate over a dictionary, you can use for..in loop or Object.keys() method. Be aware that the order of the keys is not guaranteed.

Dealing with Unknown Keys

If you don’t know whether a key exists in a dictionary, use the in keyword to check before using it.

Comparison with Other Data Structures

Compared to other data structures:

Dictionary vs Array

Arrays are good when you have a list of items, and the order matters. Dictionaries are better when you need to associate keys with values and lookup speed is important.

Dictionary vs Set

Sets are good when you have a list of unique items, and you only need to check membership. Dictionaries are better when you need to associate keys with values.

Dictionary vs Map

In JavaScript/TypeScript, a Map is a more advanced structure that can have any type as a key, whereas in a dictionary (plain object), keys can be only strings or symbols. Maps maintain the insertion order, whereas dictionaries do not.