# Get shortest/longest string in array

**URL:** <https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [January 21, 2021, 6:26pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739 "2021-01-21T18:26:37Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ferrolho](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferrolho/32/213665_2.png) [@ferrolho](https://discourse.julialang.org/u/ferrolho)\
**Post date:** [January 21, 2021, 6:26pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/1 "2021-01-21T18:26:37Z")

</div>

What is the best way to retrieve the shortest or longest string in an array? If there are multiple candidates having the same minimum/maximum length, it’s okay to return just the first one.

This can be solved with the snippet below. Is there a better way to do it, though? Thanks in advance.

```julia
arr = ["hello", "beautiful", "world", "!"]
minl, maxl = extrema(length, arr)

shortest = filter(x -> length(x) == minl, arr) |> first
longest = filter(x -> length(x) == maxl, arr) |> first

# or even...
shortest = arr[findfirst(x -> length(x) == minl, arr)]
longest = arr[findfirst(x -> length(x) == maxl, arr)]

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 21, 2021, 6:38pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/2 "2021-01-21T18:38:20Z")

</div>

I would certainly use `findfirst` rather than `filter` here (since the latter creates a new array unnecessarily). But you could also easily write a function to do this in a single pass:

```julia
function extrema_elements(f, itr)
    i = iterate(itr)
    i === nothing && throw(ArgumentError("iterator must be nonempty"))
    xmin = xmax = i[1]
    vmin = vmax = f(xmin)
    while true
        i = iterate(itr, i[2])
        i === nothing && return (xmin, xmax)
        x = i[1]; v = f(x)
        if v < vmin
            xmin, vmin = x, v
        elseif v > vmax
            xmax, vmax = x, v
        end
    end
end

```

giving:

```julia
julia> extrema_elements(length, arr)
("!", "beautiful")

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [January 21, 2021, 6:48pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/3 "2021-01-21T18:48:52Z")

</div>

I have a feeling this can be done elegantly with Transducers.jl based on this [example](https://juliafolds.github.io/Transducers.jl/stable/tutorials/tutorial_missings/#tutorial-findminmax)

---

<div class="post-metadata">

**Author:** ![ferrolho](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferrolho/32/213665_2.png) [@ferrolho](https://discourse.julialang.org/u/ferrolho)\
**Post date:** [January 21, 2021, 6:50pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/4 "2021-01-21T18:50:54Z")

</div>

Thank you for the prompt reply, Professor. The custom function you suggest is great, especially for other collections besides arrays. For example, if instead of an array of strings our input was a set of strings, the filtering from my original post would work, and so would the `findfirst`, but it would require to first collect the set to an array; however, your function would work regardless and without unnecessary copies! 🙂

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 21, 2021, 6:53pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/5 "2021-01-21T18:53:38Z")

</div>

Note also that there is some ambiguity in the definition of “length”:

- `length(s)` is the number of Unicode characters, which might not be what you expect if the text can contain combining characters like diacritical marks, e.g. `length("naïve") == 5` but `length("naïve") == 6`. (If this confuses you, compare `collect("naïve")` to `collect("naïve")` and read about [Unicode equivalence](https://en.wikipedia.org/wiki/Unicode_equivalence).)

- `sizeof(s)` is quicker to compute than `length` (it is `O(1)` rather than `O(n)` where `n` is the length of `s`) and gives the size in bytes. (This equals the `length` for ASCII strings, but not for other characters; for example, `sizeof("🐨") == 4` because the [UTF-8 encoding](https://en.wikipedia.org/wiki/UTF-8) of this emoji character requires 4 bytes. And `sizeof("naïve") == 6` while `sizeof("naïve") == 7`!)

- `textwidth(s)` gives the length of the string as measured in terminal character cells (roughly), so that `textwidth("naïve") == 5`. However, `textwidth("🐨") == 2` because emoji are typically displayed as taking up 2 terminal cells.

- `length(Unicode.graphemes(s))` gives the number of Unicode “graphemes” in `s`, which more closely corresponds to what readers would typically consider “characters”. e.g. `length(Unicode.graphemes("naïve")) == 5` and `length(Unicode.graphemes("🐨")) == 1`. But see the next point:

- The number of on-screen symbols (“glyphs”) is ultimately determined by the font and text-rendering system. For example, the [FiraCode font](https://github.com/tonsky/FiraCode) renders the ASCII string `"->"` (2 characters, 2 bytes, 2 graphemes, textwidth 2) as a single `→` glyph via a [ligature](https://en.wikipedia.org/wiki/Orthographic_ligature).

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 21, 2021, 7:02pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/6 "2021-01-21T19:02:07Z")

</div>

> [@jling](#):
>
> I have a feeling this can be done elegantly with Transducers.jl based on this [example](https://juliafolds.github.io/Transducers.jl/stable/tutorials/tutorial_missings/#tutorial-findminmax)

You could also use `foldl`:

```julia
function extrema_elements(f, itr)
    x = first(itr); v = f(x)
    val = foldl(itr, init=(x,v,x,v)) do minmax, x
        v = f(x)
        xmin, vmin, xmax, vmax = minmax
        if v < vmin
            xmin, vmin = x, v
        elseif v > vmax
            xmax, vmax = x, v
        end
        (xmin, vmin, xmax, vmax)
    end
    return val[1], val[3]
end

```

I’m not sure whether this is clearer than an explicit loop, however.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 21, 2021, 9:13pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/7 "2021-01-21T21:13:54Z")

</div>

Another way (for the non ambiguous strings):  
PS: replaced `length` by `sizeof` for speed

```julia
arr = ["hello", "beautiful", "world", "!"]
arr[sortperm(sizeof.(arr))][1:length(arr)-1:end]
2-element Vector{String}:
 "!"
 "beautiful"

```

This variant allocates a bit less:

```julia
sortperm(sizeof.(arr)) |> z -> (arr[z[1]], arr[z[end]])
("!", "beautiful")

```

---

<div class="post-metadata">

**Author:** ![PabloZubieta](https://avatars.discourse-cdn.com/v4/letter/p/ee7513/32.png) [@PabloZubieta](https://discourse.julialang.org/u/PabloZubieta)\
**Post date:** [January 21, 2021, 11:39pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/8 "2021-01-21T23:39:28Z")

</div>

You can reduce allocation even further with

```julia
sortperm(arr, by = length)

```

---

<div class="post-metadata">

**Author:** ![Jeff\_Emanuel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeff_emanuel/32/15440_2.png) [@Jeff\_Emanuel](https://discourse.julialang.org/u/Jeff_Emanuel)\
**Post date:** [January 21, 2021, 11:47pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/9 "2021-01-21T23:47:07Z")

</div>

Why introduce O(n log n) sorting to solve a fundamentally O(n) problem?

---

<div class="post-metadata">

**Author:** ![Seif\_Shebl](https://avatars.discourse-cdn.com/v4/letter/s/eada6e/32.png) [@Seif\_Shebl](https://discourse.julialang.org/u/Seif_Shebl)\
**Post date:** [January 21, 2021, 11:49pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/10 "2021-01-21T23:49:23Z")

</div>

The naive implementation is actually hard to beat:

```julia
function minmax_elements(arr)
    minl = maxl = arr[1]
    smin = smax = sizeof(minl)
    for a in arr
        sa = sizeof(a)
        if sa > smax
            maxl, smax = a, sa
        elseif sa < smin
            minl, smin = a, sa
        end
    end
    minl, maxl
end 

arr = ["hello", "beautiful", "world", "!"];
@btime minmax_elements($arr)
  5.700 ns (0 allocations: 0 bytes)
("!", "beautiful")

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 22, 2021, 12:07am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/11 "2021-01-22T00:07:32Z")

</div>

@Seif_Shebl, you have activated the Julia turbo mode!

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 22, 2021, 12:09am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/12 "2021-01-22T00:09:59Z")

</div>

@Jeff_Emanuel, maybe for the pleasure of learning Julia?  
PS: and thanks for the insight.

---

<div class="post-metadata">

**Author:** ![aramirezreyes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aramirezreyes/32/42573_2.png) [@aramirezreyes](https://discourse.julialang.org/u/aramirezreyes)\
**Post date:** [January 22, 2021, 12:24am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/13 "2021-01-22T00:24:58Z")

</div>

What about:

```julia
julia> a = ["!","beautiful"]
2-element Vector{String}:
 "!"
 "beautiful"

julia> findmax(length,a)
(9, "beautiful")

julia> a = ["!","beautiful", "beautifil"]
3-element Vector{String}:
 "!"
 "beautiful"
 "beautifil"

julia> findmax(length,a)
(9, "beautiful")

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [January 22, 2021, 5:57am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/14 "2021-01-22T05:57:50Z")

</div>

This is actually slightly suboptimal, as it reads the first element twice.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 22, 2021, 7:38am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/15 "2021-01-22T07:38:02Z")

</div>

@aramirezreyes, I liked your solution before trying it. It issues error in Win10 Julia 1.6.0-beta1.0:

```julia
julia> findmax(length,a)
ERROR: MethodError: no method matching findmax(::typeof(length), ::Vector{String})

```

Any clues?

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [January 22, 2021, 7:49am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/16 "2021-01-22T07:49:08Z")

</div>

> [@rafael.guerra](#):
>
> `julia> findmax(length,a)`

I thought this was coming in 1.6, but actually it’s not here until 1.7: [julia/NEWS.md at master · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/master/NEWS.md#new-library-functions)

---

<div class="post-metadata">

**Author:** ![Seif\_Shebl](https://avatars.discourse-cdn.com/v4/letter/s/eada6e/32.png) [@Seif\_Shebl](https://discourse.julialang.org/u/Seif_Shebl)\
**Post date:** [January 22, 2021, 10:44am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/17 "2021-01-22T10:44:20Z")

</div>

Not a big deal in exchange for elegance. This minimal implementation is still 10X faster than `findmin` , `findmax` though.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [January 22, 2021, 11:17am UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/18 "2021-01-22T11:17:12Z")

</div>

The strange thing is that your implementation is faster than @stevengj’s when using the `sizeof` function. I would have thought those two implementations to be identical, except that the latter only evaluates the first element once, so should be marginally faster, but it’s the opposite.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 22, 2021, 12:14pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/19 "2021-01-22T12:14:32Z")

</div>

This one does not look bad:

```julia
sizeof.(arr) |> z -> arr[argmin(z)], arr[argmax(z)]

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 22, 2021, 1:06pm UTC](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739/20 "2021-01-22T13:06:32Z")

</div>

> [@DNF](#):
>
> The strange thing is that your implementation is faster than @stevengj’s when using the `sizeof` function.

In Julia 1.5, mine is about 13% slower on my machine. However, in Julia 1.6, mine is 13% _faster_ for the original 4-element array, and 24% faster for a longer array `arr2 = repeat(arr, 100)`. I’m not sure why, though; I suspect it gets into the weeds of when the compiler is inlining things etc.

[Next page](https://discourse.julialang.org/t/get-shortest-longest-string-in-array/53739.md?page=2)
