# Findfirst and eachline

**URL:** <https://discourse.julialang.org/t/findfirst-and-eachline/129165>\
**Category:** General Usage\
**Created:** [May 20, 2025, 7:58am UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165 "2025-05-20T07:58:53Z")\
**Posts on this page:** 15\
**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:** [May 20, 2025, 7:58am UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/1 "2025-05-20T07:58:54Z")

</div>

The following one liner identifies the text file row number where the word “Stack” first appears:

```julia
findfirst(r -> contains(r, "Stack"), readlines(file))

```

However, the following does not, perhaps surprisingly:

```julia
findfirst(r -> contains(r, "Stack"), eachline(file))

```

Issuing:

```julia
ERROR: MethodError: no method matching keys(::Base.EachLine{IOStream})
The function `keys` exists, but no method is defined for this combination of argument types.

```

Any clues?

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [May 20, 2025, 8:13am UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/2 "2025-05-20T08:13:42Z")

</div>

`findfirst` uses `keys` but `eachline` doesn’t have keys. You can `collect` it first probably.

---

<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:** [May 20, 2025, 8:29am UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/3 "2025-05-20T08:29:46Z")

</div>

`collect(eachline()) == readlines()`, but it would be more interesting to not have to read all the lines.

---

<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:** [May 20, 2025, 9:10am UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/4 "2025-05-20T09:10:06Z")

</div>

FWIW, the following options avoid `collect()`:

```julia
for (i, r) in enumerate(eachline(file))
   contains(r, "Stack") && return i
end

```

or:

```julia
first(first(Iterators.filter(x -> contains(x[2], "Stack"), enumerate(eachline(file)))))

```

but not as simple/nice as the wished syntax.

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [May 20, 2025, 12:13pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/5 "2025-05-20T12:13:21Z")

</div>

The latter can be rewritten in a nicer way with a comprehension:

```julia
first(ind for (ind, str) in enumerate(eachline(file)) if contains(str, "Stack"))

```

---

<div class="post-metadata">

**Author:** ![tverho](https://avatars.discourse-cdn.com/v4/letter/t/839c29/32.png) [@tverho](https://discourse.julialang.org/u/tverho)\
**Post date:** [May 20, 2025, 12:20pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/6 "2025-05-20T12:20:55Z")

</div>

I think this is a good example of where traits or interfaces would make things nicer. There could be separate implementations of `findfirst` for simple iterable sequences and ones that are maps of key=\>value pairs (e.g. `IsIterable` and `IsMap`).

---

<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:** [May 20, 2025, 12:53pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/7 "2025-05-20T12:53:51Z")

</div>

> [@tverho](#):
>
> There could be separate implementations of `findfirst` for simple iterable sequences and ones that are maps of key=\>value pairs

It’s not clear what `findfirst` should even _mean_ for non-indexable collections, in general. For example, what should it do for an unordered collection where iteration order is arbitrary? Like:

```julia
findfirst(iszero, Set(0:10))

```

I think it is better in such cases for the caller to say explicitly what they want, e.g. have an `Iterators.Enumerable(itr)` wrapper that defines `pairs` for any collection in terms of enumeration, like:

```julia
struct Enumerable{I}
    itr::I
end
Base.pairs(e::Enumerable) = enumerate(e.itr)

# plus other methods to make iterate(e::Enumerable) --> iterate(e.itr), etcetera

```

in which case you can do

```julia
julia> findfirst(iszero, Enumerable(Set(0:10)))
5

```

---

<div class="post-metadata">

**Author:** ![tverho](https://avatars.discourse-cdn.com/v4/letter/t/839c29/32.png) [@tverho](https://discourse.julialang.org/u/tverho)\
**Post date:** [May 20, 2025, 1:15pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/8 "2025-05-20T13:15:36Z")

</div>

The orderedness question is not specific to non-indexable collections. Maps can be unordered too, such as `Dict`. Currently `findfirst` doesn’t care whether the order of the keys has any significance, it just returns the first match when iterating over the keys. Unless there is motivation to change that, the same behavior could be extended to non-indexable collections.

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [May 20, 2025, 1:39pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/9 "2025-05-20T13:39:23Z")

</div>

The invariant we have right now is

```julia
k = findfirst(predicate, A)
@assert predicate(A[k])

```

That invariant is lost for non-indexable `A`.

---

<div class="post-metadata">

**Author:** ![tverho](https://avatars.discourse-cdn.com/v4/letter/t/839c29/32.png) [@tverho](https://discourse.julialang.org/u/tverho)\
**Post date:** [May 20, 2025, 6:09pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/10 "2025-05-20T18:09:29Z")

</div>

Admittedly, since `findfirst` returns an index, it perhaps doesn’t make a lot of sense to use it to a non-indexable collection.

Sometimes it’d be more convenient to have function that returns the first element that satisfies the predicate (instead of its index). In that case, indexability would not be a requirement.

---

<div class="post-metadata">

**Author:** ![HSt](https://avatars.discourse-cdn.com/v4/letter/h/b38774/32.png) [@HSt](https://discourse.julialang.org/u/HSt)\
**Post date:** [May 20, 2025, 7:03pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/11 "2025-05-20T19:03:11Z")

</div>

> [@tverho](#):
>
> Sometimes it’d be more convenient to have function that returns the first element that satisfies the predicate (instead of its index). In that case, indexability would not be a requirement.

_(edit: quote what I intended to answer to)_

maybe `Iterators.filter` + `first` does what you want?

```julia
julia> k = first(Iterators.filter(x->startswith(x,"git restore"), eachline(".bash_history")))
"git restore Presentations/"

```

According to the docs there should be no unneccessary allocations, but I have not checked my assumption.

---

<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:** [May 20, 2025, 7:18pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/12 "2025-05-20T19:18:50Z")

</div>

@HSt, note that the code shown throws the line, not its number.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 20, 2025, 7:36pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/13 "2025-05-20T19:36:08Z")

</div>

With `first(Iterators.filter(...))`, you can just throw an `enumerate` at the `eachline`:

```Julia
julia> first(Iterators.filter(((_,x),)->contains(x,"git restore"), enumerate(eachline("/Users/mbauman/.zsh_history"))))
(1289, ": 1691431753:0;git restore --staged base/abstractarray.jl")

```

(now you know what I was doing at 2pm on August 7, 2023)

---

<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:** [May 20, 2025, 7:37pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/14 "2025-05-20T19:37:24Z")

</div>

That looks similar to the [post a bit further above](https://discourse.julialang.org/t/findfirst-and-eachline/129165/4). 🙂  
Note that one additional `first()` is required to extract the line number.

---

<div class="post-metadata">

**Author:** ![HSt](https://avatars.discourse-cdn.com/v4/letter/h/b38774/32.png) [@HSt](https://discourse.julialang.org/u/HSt)\
**Post date:** [May 20, 2025, 7:55pm UTC](https://discourse.julialang.org/t/findfirst-and-eachline/129165/17 "2025-05-20T19:55:42Z")

</div>

Simplicity here tends to be more personal taste? As [4th post](https://discourse.julialang.org/t/findfirst-and-eachline/129165/4) without `enumerate`, IMHO readable and reusable:

```julia
function firstline(pred, file)
    i = 0
    for r in eachline(file)
        i += 1
        pred(r) && return i
    end
    # not found
    nothing
end

```

Here is the one-liner 🫢

```julia
julia> firstline(startswith("git restore"), ".bash_history")
34

```

_(edit:_ `firstline` _reads nicer than_ `grepfirst`_; edit: link to prev post)_
