# Find the position of a single non-matching character between two strings

**URL:** <https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560>\
**Category:** General Usage\
**Tags:** strings, unicode\
**Created:** [August 30, 2022, 5:07pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560 "2022-08-30T17:07:38Z")\
**Posts on this page:** 20\
**Page:** 1

<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:** [August 30, 2022, 5:07pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/1 "2022-08-30T17:07:38Z")

</div>

To find the position of a single non-matching character between two strings:

```julia
s1 = "part1_Z_part2.txt"
s2 = "part1_Y_part2.txt"

ix = findfirst([!=(c[1],c[2]) for c in zip(s1,s2)])

```

Is there a simpler way to write `findfirst` without creating the temporary vector? Is there a better way?

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [August 30, 2022, 5:46pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/2 "2022-08-30T17:46:49Z")

</div>

Maybe the definition of simple can be discussed, if plain old `for` loop are an option, then the following code works, is readable and doesn’t allocate. (But it is not a one-liner 😉 )

```julia
function firstdiff(s1, s2)

  if length(s1) != length(s2)
    return min(length(s1), length(s2)) + 1
  end

  for (i,(c1,c2)) in enumerate(zip(s1,s2))
    if c1 != c2
      return i
    end
  end

  return 0 
end

```

(Oh, I just noted your profile name, so it wasn’t a beginner question. For sure that solution was obvious to you anyway 🙈 )

---

<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:** [August 30, 2022, 5:54pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/3 "2022-08-30T17:54:27Z")

</div>

It is definitely better and it is simple, but not simpler.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [August 30, 2022, 6:07pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/4 "2022-08-30T18:07:17Z")

</div>

Ok, I have an obscure, (not simple), non-allocating one…

```julia
minimum( (a==b) ? typemax(Int64) : i for (i,(a,b)) in enumerate(zip(s1,s2)) )

```

😃 [It’s somehow surprisingly funny/difficult to find a good one-liner given how simple the question is.]

---

<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:** [August 30, 2022, 6:09pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/5 "2022-08-30T18:09:40Z")

</div>

No access to computer here, but does it work with a generator instead of an array comprehension? I suspect it requires indexing, though.

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [August 30, 2022, 6:12pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/6 "2022-08-30T18:12:13Z")

</div>

It doesn’t, as you suspect. `findall` needs `keys` to get the index to return, and `keys` isn’t defined for `zip` ([related (stale) issue](https://github.com/JuliaLang/julia/issues/28796))

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [August 30, 2022, 6:12pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/7 "2022-08-30T18:12:42Z")

</div>

Somehow not…

```julia
julia> findfirst( a!=b for (a,b) in zip(s1,s2) )
ERROR: MethodError: no method matching keys(::Base.Iterators.Zip{Tuple{String, String}})
Closest candidates are:
  keys(::IndexStyle, ::AbstractArray, ::AbstractArray...) at ~/julia/1.7.3/share/julia/base/abstractarray.jl:350
  keys(::Tuple) at ~/julia/1.7.3/share/julia/base/tuple.jl:72
  keys(::Tuple, ::Tuple...) at ~/julia/1.7.3/share/julia/base/tuple.jl:77

```

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [August 30, 2022, 6:15pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/8 "2022-08-30T18:15:48Z")

</div>

With @digital_carver / @DNF hints of why `zip` makes trouble… so that would work and not allocate:

```julia
findfirst( s1[i] != s2[i] for i in 1:min(length(s1),length(s2)) )

```

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [August 30, 2022, 6:19pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/9 "2022-08-30T18:19:59Z")

</div>

With the caveat that this only works for ASCII strings. For eg.

```julia
julia> s1 = "caféteria"; s2 = "cafémeria";

julia> findfirst( s1[i] != s2[i] for i in 1:min(length(s1),length(s2)) )
ERROR: StringIndexError: invalid index [5], valid nearby indices [4]=>'é', [6]=>'t'

```

---

<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:** [August 30, 2022, 6:27pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/10 "2022-08-30T18:27:22Z")

</div>

> [@digital\_carver](#):
>
> With the caveat that this only works for ASCII strings

Actually as I haved asked this with file names in mind, that are similar but for one letter, I guess Unicode should be allowed too.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [August 30, 2022, 6:39pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/11 "2022-08-30T18:39:00Z")

</div>

Good catch… Uff. Does this count? (Anyway, I should work on my thesis, so I will give up, curious what tricks people will find.)

```julia
ind = for (i,(a,b)) in enumerate(zip(s1,s2)); a == b || return i; end

```

---

<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:** [August 30, 2022, 6:47pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/12 "2022-08-30T18:47:09Z")

</div>

Note that for `String` you can probably do better (performance-wise) by comparing bytes in the `codeunits(s1)` and `codeunits(s2)` arrays, then converting the resulting byte index back to a string index with `thisind`.

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [August 30, 2022, 6:52pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/13 "2022-08-30T18:52:09Z")

</div>

That does the trick, works for the Unicode character strings too. Basically a shortened version of your original function, without the length check. That also means that when the lengths are different with one of them being a substring of the other, for eg. “julia” and “julialang”, it returns nothing - not sure if that’s okay or not for @rafael.guerra 's use case.

(Tangential, but I couldn’t find this `return` in a `for` loop documented in [the manual section on loops](https://docs.julialang.org/en/v1/manual/control-flow/#man-loops) or in REPL docstrings. Does it only work in global scope, where can I find more about it?)

---

<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:** [August 30, 2022, 6:52pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/14 "2022-08-30T18:52:20Z")

</div>

Nope, none of the solutions in this thread so far have been correct for Unicode; they confuse character indices (ala `enumerate`) with string indices.

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [August 30, 2022, 6:56pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/15 "2022-08-30T18:56:09Z")

</div>

> [@stevengj](#):
>
> they confuse character indices (ala `enumerate`) with string indices.

Oh yeah, I was gonna mention that in a now-abandoned post. By string indices you mean byte indices I presume? If it’s something user-facing, graphemes may also be the thing to consider.

---

<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:** [August 30, 2022, 7:00pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/16 "2022-08-30T19:00:45Z")

</div>

> [@digital\_carver](#):
>
> By string indices you mean byte indices I presume?

I mean indices that you can actually use to index into the string, i.e. an index `i` where `s1[i] != s2[i]` is valid, so you can use it for subsequent processing. Yes, technically this is a codeunit index (a byte index for `String`).

For example, this implementation is both faster than anything posted so far and is correct for Unicode (in that it returns a valid index or `nothing`), though it doesn’t take Unicode normalization into account:

```julia
const UTF8String = Union{String,SubString{String}}
function firstdiff_index(s1::UTF8String, s2::UTF8String)
    c1, c2 = codeunits(s1), codeunits(s2)
    @inbounds for i in 1:min(length(c1),length(c2))
        c1[i] != c2[i] && return thisind(s1, i)
    end
    return nothing
end

```

> [@digital\_carver](#):
>
> If it’s something user-facing, graphemes may also be the thing to consider.

What would the user do with a grapheme index?

---

<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:** [August 30, 2022, 7:06pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/17 "2022-08-30T19:06:30Z")

</div>

If I’m not mistaken, @SteffenPL got the simplest solution for ASCII strings in [post#8](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/8), but only @stevengj’s solution provides correct answers for Unicode strings. Thanks to all.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [August 30, 2022, 7:10pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/18 "2022-08-30T19:10:01Z")

</div>

```julia
findfirst(==(only(setdiff(s1,s2))), s1)

```

---

<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:** [August 30, 2022, 7:13pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/19 "2022-08-30T19:13:27Z")

</div>

> [@rocco\_sprmnt21](#):
>
> `findfirst(==(only(setdiff(s1,s2))), s1)`

This is \> 50\times slower than a loop, and is also somewhat different from the other solutions in that it fails if `s1` and `s2` differ in more than a single character, instead of returning the first mismatch.

_Update:_ also incorrect as [described below](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/25)

---

<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:** [August 30, 2022, 7:17pm UTC](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560/20 "2022-08-30T19:17:15Z")

</div>

Note that if you want something that works for arbitrary `AbstractString` subtypes (not just UTF-8 encodings), you could use:

```julia
function firstdiff_indices(s1::AbstractString, s2::AbstractString)
    for ((i1, c1), (i2, c2)) in zip(pairs(s1), pairs(s2))
        c1 != c2 && return (i1, i2)
    end
    return nothing
end

```

(Note that in this case you need to return two indices in general, since `s1` and `s2` might have different indexing schemes.) It’s non-allocating, but is about 5x slower than the byte-scan method for `String`.

[Next page](https://discourse.julialang.org/t/find-the-position-of-a-single-non-matching-character-between-two-strings/86560.md?page=2)
