Reputation: 625
I wonder if there's a cheap(performance wise) option to search an index of array element which meets certain criteria starting from an index?
Array.tryFindIndex method doesn't have an argument startIndex. I could do Array.skip(n) and then search there but it seems expensive to create an array just for search. How do I do this?
I looked List also doesn't have that argument. Do I have to use while ... do? Is there a nicer way?
Upvotes: 4
Views: 517
Reputation: 1
For example, I'm searching for the LF character in an array of bytes starting from some startIndex. I have some class of different functions f, where I put function find10:
module Main =
type f =
static member find10(bytes:inref<byte[]>,i:int)=
if i<bytes.Length && bytes[i]<>b10 then
f.find10(&bytes,i+m1)
else
i
The constants m1 is 1 and b10 is byte 10. Now you can refer in your code like this:
let i=f.find10(&bytes,startIndex)
Upvotes: 0
Reputation: 1991
The base libraries try to provide functions for your convenience but they cannot possibly anticipate all use cases. Nothing wrong with writing your own if need be:
module Array =
let tryFindIndexFrom i p (a : _ []) =
let rec loop k =
if k >= a.Length then None
elif p a.[k] then Some k
else loop (k + 1)
if i < 0 then None else loop i
EDIT: p
is the predicate testing the array elements. tryFindIndexFrom
has the same signature as tryFindIndex
but with the starting index added as first parameter.
EDIT 2: Added test for k < 0
for fool-proof usage.
EDIT 3: Moved test for k < 0
out of the loop as it needs to be checked only once.
Upvotes: 6
Reputation: 5751
Follow your instinct. Considering Array.skip
but noting the obvious waste of allocating a second array, you can take it one step further and generalize to the lazily evaluated Seq.skip
, compose it with the standard Seq.tryFindIndex
function and add the offset, if applicable.
let tryFindIndexMin n p =
Seq.skip n
>> Seq.tryFindIndex p
>> Option.map ((+) n)
// val tryFindIndexMin : n:int -> p:('a -> bool) -> (seq<'a> -> int option)
[ for i in 0..3 ->
[|"a"; "b"; "a"; "b"|]
|> tryFindIndexMin i ((=) "a") ]
// val it : int option list = [Some 0; Some 2; Some 2; null]
Upvotes: 2
Reputation: 10624
Here's a way to do it using a lazy sequence of array indexes:
let input = [| 'a' .. 'z' |]
seq { 4 .. input.Length - 1 }
|> Seq.tryFind (fun i -> input |> Array.tryItem i = Some 'x')
I'll leave it to you to generalise this into a helper function if you think that's necessary.
The nice thing about the current form is that it's quite flexible. You can change the maximum index easily, or search backwards, e.g. seq { input.Length - 1 .. -1 .. 4 }
.
Upvotes: 2