Source file Js_array.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
(** JavaScript Array API *)

type 'a t = 'a array
type 'a array_like

let from _ = Js_internal.notImplemented "Js.Array" "from"
let fromMap _ ~f:_ = Js_internal.notImplemented "Js.Array" "fromMap"

(* This doesn't behave the same as melange-js, since it's a runtime check so lists are represented as arrays in the runtime: isArray([1, 2]) == true *)
let isArray (_arr : 'a) = true
let length arr = Stdlib.Array.length arr

(* Mutator functions *)
let copyWithin ~to_:_ ?start:_ ?end_:_ _ = Js_internal.notImplemented "Js.Array" "copyWithin"
let fill ~value:_ ?start:_ ?end_:_ _ = Js_internal.notImplemented "Js.Array" "fill"
let pop _ = Js_internal.notImplemented "Js.Array" "pop"
let push ~value:_ _ = Js_internal.notImplemented "Js.Array" "push"
let pushMany ~values:_ _ = Js_internal.notImplemented "Js.Array" "pushMany"
let reverseInPlace _ = Js_internal.notImplemented "Js.Array" "reverseInPlace"
let sortInPlace _ = Js_internal.notImplemented "Js.Array" "sortInPlace"
let sortInPlaceWith ~f:_ _ = Js_internal.notImplemented "Js.Array" "sortInPlaceWith"
let spliceInPlace ~start:_ ~remove:_ ~add:_ _ = Js_internal.notImplemented "Js.Array" "spliceInPlace"
let removeFromInPlace ~start:_ _ = Js_internal.notImplemented "Js.Array" "removeFromInPlace"
let removeCountInPlace ~start:_ ~count:_ _ = Js_internal.notImplemented "Js.Array" "removeCountInPlace"
let shift _ = Js_internal.notImplemented "Js.Array" "shift"
let unshift ~value:_ _ = Js_internal.notImplemented "Js.Array" "unshift"
let unshiftMany ~values:_ _ = Js_internal.notImplemented "Js.Array" "unshiftMany"

(* Copying functions (ES2023) *)
let flat arr = Stdlib.Array.concat (Stdlib.Array.to_list arr)

let toReversed arr =
  let len = Stdlib.Array.length arr in
  Stdlib.Array.init len (fun i -> arr.(len - 1 - i))

(* JS default sort coerces elements to strings, which requires runtime type info *)
let toSorted _ = Js_internal.notImplemented "Js.Array" "toSorted"

let toSortedWith ~f arr =
  let copy = Stdlib.Array.copy arr in
  (* JS Array.prototype.sort is required to be stable since ES2019 *)
  Stdlib.Array.stable_sort f copy;
  copy

let toSpliced ~start ~remove ~add arr =
  let len = Stdlib.Array.length arr in
  let actual_start = if start < 0 then Stdlib.max (len + start) 0 else Stdlib.min start len in
  let skip = Stdlib.min (Stdlib.max remove 0) (len - actual_start) in
  Stdlib.Array.concat
    [ Stdlib.Array.sub arr 0 actual_start; add; Stdlib.Array.sub arr (actual_start + skip) (len - actual_start - skip) ]

let removeFrom ~start arr = toSpliced ~start ~remove:(Stdlib.Array.length arr) ~add:[||] arr
let removeCount ~start ~count arr = toSpliced ~start ~remove:count ~add:[||] arr

(* Accessor functions *)
let concat ~other:second first = Stdlib.Array.append first second
let concatMany ~arrays arr = Stdlib.Array.concat (arr :: Stdlib.Array.to_list arrays)
let includes ~value arr = Stdlib.Array.exists (fun x -> x = value) arr

let indexOf ~value ?start arr =
  let rec aux idx = if idx >= Stdlib.Array.length arr then -1 else if arr.(idx) = value then idx else aux (idx + 1) in
  match start with None -> aux 0 | Some from -> if from < 0 || from >= Stdlib.Array.length arr then -1 else aux from

let join ?sep arr =
  (* js bindings can really take in `'a array`, while native is constrained to `string array` *)
  match sep with
  | None -> Stdlib.Array.to_list arr |> String.concat ","
  | Some sep -> Stdlib.Array.to_list arr |> String.concat sep

let at ~index arr =
  let len = Stdlib.Array.length arr in
  let k = if index < 0 then len + index else index in
  if k >= 0 && k < len then Some arr.(k) else None

let lastIndexOf ~value arr =
  let rec aux idx = if idx < 0 then -1 else if arr.(idx) = value then idx else aux (idx - 1) in
  aux (Stdlib.Array.length arr - 1)

let lastIndexOfFrom ~value ~start arr =
  let rec aux idx = if idx < 0 then -1 else if arr.(idx) = value then idx else aux (idx - 1) in
  if start < 0 || start >= Stdlib.Array.length arr then -1 else aux start

let slice ?start ?end_ arr =
  let len = Stdlib.Array.length arr in
  let start = match start with None -> 0 | Some s -> s in
  let end_ = match end_ with None -> Stdlib.Array.length arr | Some e -> e in
  let s = max 0 (if start < 0 then len + start else start) in
  let e = min len (if end_ < 0 then len + end_ else end_) in
  if s >= e then [||] else Stdlib.Array.sub arr s (e - s)

let copy = Stdlib.Array.copy
let toString _ = Js_internal.notImplemented "Js.Array" "toString"
let toLocaleString _ = Js_internal.notImplemented "Js.Array" "toLocaleString"

(* Iteration functions *)
let everyi ~f arr =
  let len = Stdlib.Array.length arr in
  let rec aux idx = if idx >= len then true else if f arr.(idx) idx then aux (idx + 1) else false in
  aux 0

let every ~f arr =
  let len = Stdlib.Array.length arr in
  let rec aux idx = if idx >= len then true else if f arr.(idx) then aux (idx + 1) else false in
  aux 0

let filter ~f arr = arr |> Stdlib.Array.to_list |> List.filter f |> Stdlib.Array.of_list
let filteri ~f arr = arr |> Stdlib.Array.to_list |> List.filteri (fun i a -> f a i) |> Stdlib.Array.of_list

let findi ~f arr =
  let len = Stdlib.Array.length arr in
  let rec aux idx = if idx >= len then None else if f arr.(idx) idx then Some arr.(idx) else aux (idx + 1) in
  aux 0

let find ~f arr =
  let len = Stdlib.Array.length arr in
  let rec aux idx = if idx >= len then None else if f arr.(idx) then Some arr.(idx) else aux (idx + 1) in
  aux 0

let findIndexi ~f arr =
  let len = Stdlib.Array.length arr in
  let rec aux idx = if idx >= len then -1 else if f arr.(idx) idx then idx else aux (idx + 1) in
  aux 0

let findIndex ~f arr =
  let len = Stdlib.Array.length arr in
  let rec aux idx = if idx >= len then -1 else if f arr.(idx) then idx else aux (idx + 1) in
  aux 0

let findLast ~f arr =
  let rec aux idx = if idx < 0 then None else if f arr.(idx) then Some arr.(idx) else aux (idx - 1) in
  aux (Stdlib.Array.length arr - 1)

let findLasti ~f arr =
  let rec aux idx = if idx < 0 then None else if f arr.(idx) idx then Some arr.(idx) else aux (idx - 1) in
  aux (Stdlib.Array.length arr - 1)

let findLastIndex ~f arr =
  let rec aux idx = if idx < 0 then -1 else if f arr.(idx) then idx else aux (idx - 1) in
  aux (Stdlib.Array.length arr - 1)

let findLastIndexi ~f arr =
  let rec aux idx = if idx < 0 then -1 else if f arr.(idx) idx then idx else aux (idx - 1) in
  aux (Stdlib.Array.length arr - 1)

let entries arr = Js_iterator.make (Seq.mapi (fun i v -> (i, v)) (Stdlib.Array.to_seq arr))
let keys arr = Js_iterator.make (Seq.init (Stdlib.Array.length arr) (fun i -> i))
let values arr = Js_iterator.make (Stdlib.Array.to_seq arr)
let forEach ~f arr = Stdlib.Array.iter f arr
let forEachi ~f arr = Stdlib.Array.iteri (fun i a -> f a i) arr
let map ~f arr = Stdlib.Array.map f arr
let mapi ~f arr = Stdlib.Array.mapi (fun i a -> f a i) arr

let reduce ~f ~init arr =
  let r = ref init in
  for i = 0 to length arr - 1 do
    r := f !r (Stdlib.Array.unsafe_get arr i)
  done;
  !r

let reducei ~f ~init arr =
  let r = ref init in
  for i = 0 to length arr - 1 do
    r := f !r (Stdlib.Array.unsafe_get arr i) i
  done;
  !r

let reduceRight ~f ~init arr =
  let r = ref init in
  for i = length arr - 1 downto 0 do
    r := f !r (Stdlib.Array.unsafe_get arr i)
  done;
  !r

let reduceRighti ~f ~init arr =
  let r = ref init in
  for i = length arr - 1 downto 0 do
    r := f !r (Stdlib.Array.unsafe_get arr i) i
  done;
  !r

let some ~f arr =
  let n = Stdlib.Array.length arr in
  let rec loop i = if i = n then false else if f (Stdlib.Array.unsafe_get arr i) then true else loop (succ i) in
  loop 0

let somei ~f arr =
  let n = Stdlib.Array.length arr in
  let rec loop i = if i = n then false else if f (Stdlib.Array.unsafe_get arr i) i then true else loop (succ i) in
  loop 0

let unsafe_get arr idx = Stdlib.Array.unsafe_get arr idx
let unsafe_set arr idx item = Stdlib.Array.unsafe_set arr idx item