File size: 2,455 Bytes
b4554b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a67920
 
b4554b8
 
 
6a67920
b4554b8
 
 
 
 
6a67920
b4554b8
 
6a67920
b4554b8
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<!-- livebook:{"app_settings":{"access_type":"public","output_type":"rich","show_source":true,"slug":"rune-finder"},"file_entries":[{"name":"UnicodeData.txt","type":"url","url":"https://raw.githubusercontent.com/ramalho/rf/master/elixir/UnicodeData.txt"}]} -->

# Rune finder

```elixir
Mix.install([
  {:kino, "~> 0.12.3"}
])
```

## About this

```elixir
import Kino.Shorts
```

````elixir
markdown("""
<details>
<summary>About this app</summary>

Find Unicode characters by name.

For example, for `face cat`

```
U+1F431	๐Ÿฑ	CAT FACE
U+1F638	๐Ÿ˜ธ	GRINNING CAT FACE WITH SMILING EYES
U+1F639	๐Ÿ˜น	CAT FACE WITH TEARS OF JOY
U+1F63A	๐Ÿ˜บ	SMILING CAT FACE WITH OPEN MOUTH
U+1F63B	๐Ÿ˜ป	SMILING CAT FACE WITH HEART-SHAPED EYES
U+1F63C	๐Ÿ˜ผ	CAT FACE WITH WRY SMILE
U+1F63D	๐Ÿ˜ฝ	KISSING CAT FACE WITH CLOSED EYES
U+1F63E	๐Ÿ˜พ	POUTING CAT FACE
U+1F63F	๐Ÿ˜ฟ	CRYING CAT FACE
U+1F640	๐Ÿ™€	WEARY CAT FACE
```

</details>
""")
````

## Code

```elixir
content =
  Kino.FS.file_path("UnicodeData.txt")
  |> File.read!()
```

```elixir
defmodule RuneFinder do
  defp to_rune(code, name) do
    rune = <<String.to_integer(code, 16)::utf8>>
    "U+#{code}\t#{rune}\t#{name}"
  end

  defp select(line_stream, query_words) do
    Enum.map(line_stream, fn line ->
      [code, name | _] = String.split(line, ";")

      if MapSet.subset?(query_words, tokenize(name)), do: to_rune(code, name)
    end)
    |> Enum.reject(&(&1 == nil))
  end

  defp find(query_words) do
    Kino.FS.file_path("UnicodeData.txt")
    |> File.stream!()
    |> select(query_words)
  end

  defp tokenize(text) do
    text
    |> String.replace("-", " ")
    |> String.split()
    |> MapSet.new()
  end

  def main(args) do
    args
    |> String.upcase()
    |> tokenize
    |> find
  end
end
```

```elixir
form =
  Kino.Control.form(
    [
      name: Kino.Input.text("Unicode characters name", default: "face cat")
    ],
    submit: "Find"
  )
```

```elixir
output_frame = frame()

Kino.listen(form, fn event ->
  origin = event.origin
  Kino.Frame.clear(output_frame, to: origin)

  case event.data.name do
    "" ->
      Kino.Frame.render(output_frame, "Please provide words to find.", to: origin)

    _ ->
      runes = RuneFinder.main(event.data.name)

      Enum.each(runes, fn rune ->
        Kino.Frame.append(output_frame, text(rune), to: origin)
      end)

      Kino.Frame.append(output_frame, text("\n#{Enum.count(runes)} found"), to: origin)
  end
end)

output_frame
```