How do I generically traverse an array in ReScript?

655 Views Asked by At

Let's say I want to iterate over an array in a way that isn't well-supported by the Js/Belt standard library functions. For example, perhaps I need to examine pairs of elements at a time. With a list, this is straightforward to do in a recursive style:

let rec findDouble = (list) => switch list {
| list{a, b, ..._} when a == b => a
| list{_, b, ...rest} => findDouble(list{b, ...rest})
| _ => 0
}
list{7, 9, 10, 10, 11, 13} |> findDouble |> Js.log  // 10

However, ReScript seems to gently discourage lists in favor of arrays (see: the clumsier list syntax and the absence of list equivalents of certain standard library functions like Belt.Map.fromArray), so I'm not sure if converting an array to a list just to use this style is idiomatic - especially if the function produces a list that must then turn back into an array.

Of course I can use mutability to implement the function in a traditional imperative way:

let findDouble = (arr) => {
  let idx = ref(1)
  let answer = ref(0)

  while (idx.contents < Js.Array.length(arr)) && (answer.contents == 0) {
    if arr[idx.contents] == arr[idx.contents - 1] {
      answer := arr[idx.contents]
    }
    idx := idx.contents + 1
  }
  answer.contents
}
[7, 9, 10, 10, 11, 13] |> findDouble |> Js.log  // 10

But this is ugly and runs counter to the functional bones of ReScript.

What is a clean, idiomatic way to implement this function?

1

There are 1 best solutions below

0
On BEST ANSWER

You can still use recursion, just with incrementing an index instead of using the tail of the list:

let findDouble = arr => {
  let rec loop = idx =>
    if idx >= Array.length(arr) {
      0
    } else if arr[idx] == arr[idx - 1] {
      arr[idx]
    } else {
      loop(idx + 1)
    }

  loop(1)
}