DIBIAGG.IO is open source!

Check it out on Github.

A logo with a circle, two vertical lines, and another circle, representing the letters db

Implement Frequently Used Emojis with LFU Cache

Anthony Di Biaggio

Cover Image for Implement Frequently Used Emojis with LFU Cache

Anthony Di Biaggio

tutorial
coding-stuff
dsa

While typing on my phone, I started getting curious about how the frequently used emojis feature is implemented. I was surprised that there wasn't some information out there about it (well, it is Apple, so not really), so it's time to put it out there!

The idea is that we have some set of n emojis, and we want to cache a subset of those emojis that are used the most. We could imagine attaching a usage count to each emoji, and saying that we want the k emojis with the highest count to be stored in this cache, with k <= n. Ideally, we want to be able to add to this cache and get elements from it in constant time.

Luckily for us, this is pretty trivial since this exact problem gets brought up in operating systems and there are a million solutions for it - see here.

First, we will implement an LFU cache in JavaScript. It will keep track of a few things. It will keep track of min, which is the smallest usage count currently in the cache. It has a capacity, which is the maximum amount of items that can exist in the cache. values is a Map that stores the key value pairs, counts stores each key and its respective usage count, and sets keeps track of each count and which keys have that count.

1

To get an element from the cache, we need to do a bit of bookkeeping each time.

1

We check that key exists in the cache, and continue accordingly. Then we update the count, and grab the count set that key is currently in. We move the element from that set to the set of one count higher, and create that set if needed. We also update the min, since we are changing the count of key. Finally, we return the value of key. Adding is about as simple:

1

If key already exists in the cache, then we update the value and get the key, to reflect that it has been used again. Otherwise, if the cache is full, we have to figure out which element to evict. This is why we are keeping track of min - we can just get the count set that has the minimum count, since we are evicting the least frequently used item. We just take the first element from that set and remove it to make room for the new item. The nice thing about Set in JavaScript is that it maintains insertion order, so we are guaranteed that the first element in evictSet is the oldest. We also delete the evicted item from values and counts. Then, we initialize the new item in the cache, adding it to values and counts, and resetting min to 0, since the new item hasn't been accessed yet. Then, we add it to the count set of zero, and we are done! We have a data structure which maintains its elements in order of usage frequency, where items can both be added and retrieved in O(1) time.

Now, if we are going to use this in React, we are going to need to be able to get the elements as an array, so here is a short function to do that:

1

This function simply iterates over sets and concats them onto a list. We pass in a parameter ascending to determine whether we want the list in ascending or descending order in terms of usage count.

Now that we have the data structure figured out, we can get to the emoji part. I'll make a simple React component to demonstrate.

1

For hooks, we will just have two pieces of state - cachedEmojis, which are the emojis present in the cache, e.g. the most frequently used emojis. Also, we have emojiInputs which is just all of the emojis that have been inputted. cache is a LFUCache ref - it would be a good idea to abstract the cache away into a hook, but for simplicity we will just hold it in that ref.

1

Here we have the emojis that can be used to add to the input. When an emoji is clicked, it just adds to emojiInputs, adds to cache, and refreshes the frequently used emojis stored in cachedEmojis. The rest of the component renders all of this stuff and looks like this:

1

It just creates a ChakraUI Grid which holds the frequently used emojis and the entire list of emojis, as well as a Input for the emoji input. And here it is in action!

Note: since this component just renders the emojis as text, it may look very janky depending on the browser and/or device you are viewing from. A better approach would be to render the emojis as <img> so that the width/height can be fixed.

Frequently Used
All Emojis
👦🏼
👦🏽
👦🏾
👦🏿
👧
👧🏻
👧🏼
👧🏽
👧🏾
👧🏿
👨
👨🏻
👨🏼
👨🏽
👨🏾
👨🏿
👩
👩🏻
👩🏼
👩🏽
👩🏾
👩🏿
👴
👴🏻
👴🏼
👴🏽
👴🏾
👴🏿
👵
👵🏻
👵🏼
👵🏽
👵🏾
👵🏿
👶
👶🏻
👶🏼
👶🏽
👶🏾
👶🏿
👼
👼🏻
👼🏼
👼🏽
👼🏾
👼🏿
👮
👮🏻
👮🏼
👮🏽
👮🏾
👮🏿
🕵
🕵🏻
🕵🏼
🕵🏽
🕵🏾
🕵🏿
💂
💂🏻
💂🏼
💂🏽
💂🏾
💂🏿
👷
👷🏻
👷🏼
👷🏽
👷🏾
👷🏿
👳
👳🏻
👳🏼
👳🏽
👳🏾
👳🏿
👱
👱🏻
👱🏼
👱🏽
👱🏾
👱🏿
🎅
🎅🏻
🎅🏼
🎅🏽
🎅🏾
🎅🏿
🤶
🤶🏻
🤶🏼
🤶🏽
🤶🏾
🤶🏿
👸
👸🏻
👸🏼
👸🏽
👸🏾
👸🏿
🤴
🤴🏻
🤴🏼
🤴🏽
🤴🏾
🤴🏿
👰
👰🏻
👰🏼
👰🏽
👰🏾
👰🏿
🤵
🤵🏻
🤵🏼
🤵🏽
🤵🏾
🤵🏿
🤰
🤰🏻
🤰🏼
🤰🏽
🤰🏾
🤰🏿
👲
👲🏻
👲🏼
👲🏽
👲🏾
👲🏿
🙍
🙍🏻
🙍🏼
🙍🏽
🙍🏾
🙍🏿
🙎
🙎🏻
🙎🏼
🙎🏽
🙎🏾
🙎🏿
🙅
🙅🏻
🙅🏼
🙅🏽
🙅🏾
🙅🏿
🙆
🙆🏻
🙆🏼
🙆🏽
🙆🏾
🙆🏿
💁
💁🏻
💁🏼
💁🏽
💁🏾
💁🏿
🙋
🙋🏻
🙋🏼
🙋🏽
🙋🏾
🙋🏿
🙇
🙇🏻
🙇🏼
🙇🏽
🙇🏾
🙇🏿
🤦
🤦🏻
🤦🏼
🤦🏽
🤦🏾
🤦🏿
🤷
🤷🏻
🤷🏼
🤷🏽
🤷🏾
🤷🏿
💆
💆🏻
💆🏼
💆🏽
💆🏾
💆🏿
💇
💇🏻
💇🏼
💇🏽
💇🏾
💇🏿
🚶
🚶🏻
🚶🏼
🚶🏽
🚶🏾
🚶🏿
🏃
🏃🏻
🏃🏼
🏃🏽
🏃🏾
🏃🏿
💃
💃🏻
💃🏼
💃🏽
💃🏾
💃🏿
🕺
🕺🏻
🕺🏼
🕺🏽
🕺🏾
🕺🏿
👯
🕴
🗣
👤
👥
🤺
🏇
🏂
🏌
🏄
🏄🏻
🏄🏼
🏄🏽
🏄🏾
🏄🏿
🚣
🚣🏻
🚣🏼
🚣🏽
🚣🏾
🚣🏿
🏊
🏊🏻
🏊🏼
🏊🏽
🏊🏾
🏊🏿
⛹🏻
⛹🏼
⛹🏽
⛹🏾
⛹🏿
🏋
🏋🏻
🏋🏼
🏋🏽
🏋🏾
🏋🏿
🚴
🚴🏻
🚴🏼
🚴🏽
🚴🏾
🚴🏿
🚵
🚵🏻
🚵🏼
🚵🏽
🚵🏾
🚵🏿
🏎
🏍
🤸
🤸🏻
🤸🏼
🤸🏽
🤸🏾
🤸🏿
🤼
🤼🏻
🤼🏼
🤼🏽
🤼🏾
🤼🏿
🤽
🤽🏻
🤽🏼
🤽🏽
🤽🏾
🤽🏿
🤾
🤾🏻
🤾🏼
🤾🏽
🤾🏾
🤾🏿
🤹
🤹🏻
🤹🏼
🤹🏽
🤹🏾
🤹🏿
👫
👬
👭
💏
👩‍❤️‍💋‍👨
👨‍❤️‍💋‍👨
👩‍❤️‍💋‍👩
💑
👩‍❤️‍👨
👨‍❤️‍👨
👩‍❤️‍👩
👪
👨‍👩‍👦
👨‍👩‍👧
👨‍👩‍👧‍👦
👨‍👩‍👦‍👦
👨‍👩‍👧‍👧
👨‍👨‍👦
👨‍👨‍👧
👨‍👨‍👧‍👦
👨‍👨‍👦‍👦
👨‍👨‍👧‍👧
👩‍👩‍👦
👩‍👩‍👧
👩‍👩‍👧‍👦
👩‍👩‍👦‍👦
👩‍👩‍👧‍👧
🏻
🏼
🏽
🏾
🏿
💪
💪🏻
💪🏼
💪🏽
💪🏾
💪🏿
🤳
🤳🏻
🤳🏼
🤳🏽
🤳🏾
🤳🏿
👈
👈🏻
👈🏼
👈🏽
👈🏾
👈🏿
👉
👉🏻
👉🏼
👉🏽
👉🏾
👉🏿
☝🏻
☝🏼
☝🏽
☝🏾
☝🏿
👆
👆🏻
👆🏼
👆🏽
👆🏾
👆🏿
🖕
🖕🏻
🖕🏼
🖕🏽
🖕🏾
🖕🏿
👇
👇🏻
👇🏼
👇🏽
👇🏾
👇🏿
✌🏻
✌🏼
✌🏽
✌🏾
✌🏿
🤞
🤞🏻
🤞🏼
🤞🏽
🤞🏾
🤞🏿
🖖
🖖🏻
🖖🏼
🖖🏽

Note that this doesn't behave exactly the same way as iOS or Android's frequently used emoji feature does. There are some UX enhancements that can be made - for example, if this emoji picker was put in a keyboard or a popup, deferring the cache refresh until it is closed so that the emojis don't move around while you are typing. The algorithm for determining what gets cached is also certainly more complex than this, but this is just meant to be a straightforward tutorial that shows how you can design a data structure and implement it in an interface. Hopefully you learned something, and if you made it all the way to the end, congratulations!!

Older post

Newer post

GET IN TOUCH