Packages

emoji

Find, count, extract and strip emoji in text, with flags, skin tones and joined sequences each counted as one.

import "std:emoji"

Emoji are trickier than they look. A flag is two code points, a thumbs up with a skin tone is two, and a family is five (three people joined by two invisible joiners). Counting them with .len or splitting a string by character gets this wrong. std:emoji knows the rules: each of those is one emoji.

Detecting and counting#

main.osl
import "std:emoji"

string message = "Great work ๐Ÿ‘๐Ÿฝ see you in ๐Ÿ‡ฌ๐Ÿ‡ง"
log emoji.hasEmoji(message)
log emoji.hasEmoji("no emoji here")
log emoji.count(message)
Click the code to edit it

The text's .len counts every code point that went into the emoji:

main.osl
import "std:emoji"

string family = "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง"
log emoji.count(family)
log family.len
Click the code to edit it

Extracting#

extract returns each emoji in order, whole:

main.osl
import "std:emoji"

string post = "Hi ๐Ÿ‘‹๐Ÿฝ from ๐Ÿ‡ฌ๐Ÿ‡ง and ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง, keycap 1๏ธโƒฃ and ๐Ÿ™‚"
log emoji.extract(post)
log emoji.extract("none")
Click the code to edit it

Counting how often each one appears is then a few lines of map:

main.osl
import "std:emoji"

string chat = "๐Ÿ‘ ๐Ÿ‘ ๐ŸŽ‰ ๐Ÿ‘ ๐ŸŽ‰ ๐Ÿ˜€"
string[int] tally = {}
for e in emoji.extract(chat) (
  tally[e] = (tally[e] ?? 0) + 1
)
for e in tally.getKeys().toSorted() (
  log e tally[e]
)
Click the code to edit it

Removing and replacing#

remove deletes every emoji. replace swaps each one for text:

main.osl
import "std:emoji"

string text = "Launch day ๐Ÿš€ !"
log emoji.remove(text)
log emoji.replace(text, "[emoji]")
log emoji.replace("a๐Ÿ™‚b๐Ÿ™ƒc", "")
Click the code to edit it

Removing leaves the spaces around the emoji, so tidy up afterwards with .trim().

Asking what a string is#

main.osl
import "std:emoji"

log emoji.isFlag("๐Ÿ‡ฌ๐Ÿ‡ง") emoji.isFlag("๐Ÿ™‚")
log emoji.isOnlyEmoji("๐Ÿ™‚๐Ÿ™‚") emoji.isOnlyEmoji("a๐Ÿ™‚")
log emoji.isZWJSequence("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง") emoji.isZWJSequence("๐Ÿ™‚")
Click the code to edit it

A ZWJ sequence is several emoji joined by a zero-width joiner into one picture, such as a family or a profession.

Skin tones#

hasSkinTone tells whether a skin-tone modifier is present, and findSkinTone names the first one: "light", "medium-light", "medium", "medium-dark" or "dark", or null when there is none:

main.osl
import "std:emoji"

log emoji.hasSkinTone("๐Ÿ‘‹๐Ÿฝ")
log emoji.findSkinTone("๐Ÿ‘‹๐Ÿฝ")
log emoji.findSkinTone("๐Ÿ‘‹") ?? "none"
Click the code to edit it

Validating a reaction#

A reaction field should hold exactly one emoji and nothing else. isValidReaction checks that:

main.osl
import "std:emoji"

string[] tries = ["๐Ÿ‘", "๐Ÿ‘๐Ÿ‘", "a", "๐Ÿ‡ฌ๐Ÿ‡ง", "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง"]
for t in tries (
  log t emoji.isValidReaction(t)
)
Click the code to edit it
Quick check

What does emoji.count("๐Ÿ‡ฌ๐Ÿ‡ง") return?

Your turn

Clean a status

Finish clean so it removes every emoji from a status and trims the spaces they leave behind.

main.osl
Ctrl Enter runs
Expected output
Launch
Party
plain

API reference#

14 members, generated from the std:emoji source of OSL 9.3.0.

Functions

#emoji.count(string text) int

count returns how many emoji text contains. A flag, keycap, toned emoji or ZWJ sequence such as a family counts as one.

#emoji.extract(string text) string[]

extract returns every emoji in text in order, each flag, keycap, toned emoji or ZWJ sequence as one string; [] when there are none.

#emoji.findSkinTone(string text) string?

findSkinTone returns the name of the first skin tone in text: "light", "medium-light", "medium", "medium-dark" or "dark", or null when text has no skin-tone modifier.

#emoji.hasEmoji(string text) boolean

hasEmoji reports whether text contains at least one emoji.

#emoji.hasSkinTone(string text) boolean

hasSkinTone reports whether text contains a skin-tone modifier.

#emoji.isEmoji(any text) boolean

Deprecated. Deprecated: use emoji.hasEmoji(text) instead.

isEmoji reports whether text contains at least one emoji.

#emoji.isFlag(string text) boolean

isFlag reports whether text is exactly one flag: a pair of regional indicator symbols (the pair is two code points, eight bytes) or a tag sequence flag such as England's.

#emoji.isOnlyEmoji(string text) boolean

isOnlyEmoji reports whether text is one or more emoji with nothing else, not even spaces. An empty string is not.

#emoji.isValidReaction(string text) boolean

isValidReaction reports whether text can be used as a reaction: exactly one emoji, or an originChats custom reaction "originChats:<emoji>//" wrapping exactly one emoji.

#emoji.isZWJSequence(string text) boolean

isZWJSequence reports whether text contains a zero-width joiner (U+200D), which joins emoji such as a family or a profession into one.

#emoji.onlyEmoji(any text) boolean

Deprecated. Deprecated: use emoji.isOnlyEmoji(text) instead.

onlyEmoji reports whether text is one or more emoji and nothing else.

#emoji.remove(string text) string

remove returns text with every emoji removed.

#emoji.replace(string text, string replacement) string

replace returns text with each emoji replaced by replacement, taken literally: "$1" or "${x}" in it is not expanded.

#emoji.skinTone(any text) string

Deprecated. Deprecated: use emoji.findSkinTone(text) instead, which returns null when there is no skin tone.

skinTone returns the name of the first skin tone in text, or "none".