Skip to content

Array additions #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a882beb
feat(array): add findMap
glennsl Feb 18, 2023
31c8f64
feat(array): add init
glennsl Feb 18, 2023
307da89
feat(array): add make
glennsl Feb 18, 2023
12402ae
feat(array): add keepSome
glennsl Feb 18, 2023
2370ea6
refactor(array): bind to flatMap instead of reimplementing it
glennsl Feb 18, 2023
aa06078
refactor(array/make): use fillAllInPlace instead of manual loop
glennsl Feb 18, 2023
e02724c
feat(array): use labelled argument for length in make and init
glennsl Feb 19, 2023
c8a67b5
refactor(array): rename init -> fromInitializer
glennsl Feb 19, 2023
93110d0
test(array): add tests for non-native functions
glennsl Feb 26, 2023
0090ab6
refactor(array): bind to native reduce instead of reimplementing it
glennsl Feb 26, 2023
a2e2df9
refactor(array): replace reduceReverse with native binding to reduceR…
glennsl Feb 26, 2023
3042949
refactor(array): use native reduceWithIndex instead of reimplementing it
glennsl Feb 26, 2023
8eb7433
feat(array): add reduceRightWithIndex
glennsl Feb 26, 2023
b2f539e
docs(array): add docs for make, fromInitializer, findMap and keepSome
glennsl Feb 26, 2023
ffc18ee
docs(changelog): update changelog for array additions
glennsl Feb 26, 2023
85ce08b
Merge branch 'main' into feat/array-additions
glennsl Feb 26, 2023
b80a80f
docs(array): fix docstring fro keepSome
glennsl Feb 28, 2023
6d4f256
fix(array): reorder args of reduce functions for better te inference
glennsl Feb 28, 2023
616279f
refactor(array): consistent type variable naming in reduce functions
glennsl Feb 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
- Change `Float.parseFloat` signature. Now accepts only string. https://github.com/rescript-association/rescript-core/pull/54
- Add `getExn`, `getUnsafe`, `getWithDefault`, `map`, `mapWithDefault` and `flatMap` to `Nullable`. https://github.com/rescript-association/rescript-core/pull/67
- Add `getExn`, `getUnsafe`, `getWithDefault`, `map`, `mapWithDefault` and `flatMap` to `Null`. https://github.com/rescript-association/rescript-core/pull/73
- Add `make`, `fromInitializer`, `findMap`, `keepSome`, `reduceRight` and `reduceRightWithIndex`. https://github.com/rescript-association/rescript-core/pull/49
- Remove `reduceReverse` in favor of `reduceRight`. https://github.com/rescript-association/rescript-core/pull/49
- Fixed type signatures of `reduce` and `reduceWithIndex`. https://github.com/rescript-association/rescript-core/pull/49

### Documentation

Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
"clean": "rescript clean",
"build": "rescript",
"watch": "rescript build -w",
"test": "node test/PromiseTest.mjs && node test/TempTests.mjs"
"test": "node test/TestSuite.mjs && node test/TempTests.mjs"
},
"keywords": [
"rescript"
],
"keywords": ["rescript"],
"homepage": "https://github.com/rescript-association/rescript-core",
"author": "ReScript Team",
"license": "MIT",
Expand Down
78 changes: 44 additions & 34 deletions src/Core__Array.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@
import * as Curry from "rescript/lib/es6/curry.js";
import * as Js_math from "rescript/lib/es6/js_math.js";
import * as Caml_option from "rescript/lib/es6/caml_option.js";
import * as Caml_splice_call from "rescript/lib/es6/caml_splice_call.js";

function make(length, x) {
if (length <= 0) {
return [];
}
var arr = new Array(length);
arr.fill(x);
return arr;
}

function fromInitializer(length, f) {
if (length <= 0) {
return [];
}
var arr = new Array(length);
for(var i = 0; i < length; ++i){
arr[i] = Curry._1(f, i);
}
return arr;
}

function indexOfOpt(arr, item) {
var index = arr.indexOf(item);
Expand All @@ -27,33 +46,6 @@ function sort(arr, cmp) {
return result;
}

function reduce(a, x, f) {
var f$1 = Curry.__2(f);
var r = x;
for(var i = 0 ,i_finish = a.length; i < i_finish; ++i){
r = f$1(r, a[i]);
}
return r;
}

function reduceWithIndex(a, x, f) {
var f$1 = Curry.__3(f);
var r = x;
for(var i = 0 ,i_finish = a.length; i < i_finish; ++i){
r = f$1(r, a[i], i);
}
return r;
}

function reduceReverse(a, x, f) {
var f$1 = Curry.__2(f);
var r = x;
for(var i = a.length - 1 | 0; i >= 0; --i){
r = f$1(r, a[i]);
}
return r;
}

function findIndexOpt(array, finder) {
var index = array.findIndex(finder);
if (index !== -1) {
Expand Down Expand Up @@ -108,22 +100,40 @@ function filterMap(a, f) {
return r;
}

function flatMap(a, f) {
return Caml_splice_call.spliceObjApply([], "concat", [a.map(f)]);
function keepSome(__x) {
return filterMap(__x, (function (x) {
return x;
}));
}

function findMap(arr, f) {
var _i = 0;
while(true) {
var i = _i;
if (i === arr.length) {
return ;
}
var r = Curry._1(f, arr[i]);
if (r !== undefined) {
return r;
}
_i = i + 1 | 0;
continue ;
};
}

export {
make ,
fromInitializer ,
sort ,
indexOfOpt ,
lastIndexOfOpt ,
reduce ,
reduceReverse ,
reduceWithIndex ,
findIndexOpt ,
reverse ,
filterMap ,
keepSome ,
shuffle ,
shuffleInPlace ,
flatMap ,
findMap ,
}
/* No side effect */
90 changes: 51 additions & 39 deletions src/Core__Array.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@new external makeUninitializedUnsafe: int => array<'a> = "Array"
@set external truncateToLengthUnsafe: (array<'a>, int) => unit = "length"
external getUnsafe: (array<'a>, int) => 'a = "%array_unsafe_get"
external setUnsafe: (array<'a>, int, 'a) => unit = "%array_unsafe_set"

Expand All @@ -11,6 +13,32 @@ external fromArrayLikeWithMap: (Js.Array2.array_like<'a>, 'a => 'b) => array<'b>
@val external fromIterator: Core__Iterator.t<'a> => array<'a> = "Array.from"
@val external fromIteratorWithMap: (Core__Iterator.t<'a>, 'a => 'b) => array<'b> = "Array.from"

@send external fillAllInPlace: (array<'a>, 'a) => unit = "fill"

@send external fillInPlaceToEnd: (array<'a>, 'a, ~start: int) => unit = "fill"

@send external fillInPlace: (array<'a>, 'a, ~start: int, ~end: int) => unit = "fill"

let make = (~length, x) =>
if length <= 0 {
[]
} else {
let arr = makeUninitializedUnsafe(length)
arr->fillAllInPlace(x)
arr
}

let fromInitializer = (~length, f) =>
if length <= 0 {
[]
} else {
let arr = makeUninitializedUnsafe(length)
for i in 0 to length - 1 {
arr->setUnsafe(i, f(i))
}
arr
}

@val external isArray: 'a => bool = "Array.isArray"

@get external length: array<'a> => int = "length"
Expand All @@ -23,12 +51,6 @@ external copyWithinToEnd: (array<'a>, ~target: int, ~start: int) => array<'a> =
@send
external copyWithin: (array<'a>, ~target: int, ~start: int, ~end: int) => array<'a> = "copyWithin"

@send external fillAllInPlace: (array<'a>, 'a) => unit = "fill"

@send external fillInPlaceToEnd: (array<'a>, 'a, ~start: int) => unit = "fill"

@send external fillInPlace: (array<'a>, 'a, ~start: int, ~end: int) => unit = "fill"

@send external pop: array<'a> => option<'a> = "pop"

@send external push: (array<'a>, 'a) => unit = "push"
Expand Down Expand Up @@ -105,35 +127,12 @@ let sort = (arr, cmp) => {
@send external map: (array<'a>, 'a => 'b) => array<'b> = "map"
@send external mapWithIndex: (array<'a>, ('a, int) => 'b) => array<'b> = "map"

let reduceU = (a, x, f) => {
let r = ref(x)
for i in 0 to length(a) - 1 {
r.contents = f(. r.contents, getUnsafe(a, i))
}
r.contents
}

let reduce = (a, x, f) => reduceU(a, x, (. a, b) => f(a, b))

let reduceWithIndexU = (a, x, f) => {
let r = ref(x)
for i in 0 to length(a) - 1 {
r.contents = f(. r.contents, getUnsafe(a, i), i)
}
r.contents
}

let reduceWithIndex = (a, x, f) => reduceWithIndexU(a, x, (. a, b, c) => f(a, b, c))

let reduceReverseU = (a, x, f) => {
let r = ref(x)
for i in length(a) - 1 downto 0 {
r.contents = f(. r.contents, getUnsafe(a, i))
}
r.contents
}

let reduceReverse = (a, x, f) => reduceReverseU(a, x, (. a, b) => f(a, b))
@send external reduce: (array<'b>, ('a, 'b) => 'a, 'a) => 'a = "reduce"
@send external reduceWithIndex: (array<'b>, ('a, 'b, int) => 'a, 'a) => 'a = "reduce"
@send
external reduceRight: (array<'b>, ('a, 'b) => 'a, 'a) => 'a = "reduceRight"
@send
external reduceRightWithIndex: (array<'b>, ('a, 'b, int) => 'a, 'a) => 'a = "reduceRight"

@send external some: (array<'a>, 'a => bool) => bool = "some"
@send external someWithIndex: (array<'a>, ('a, int) => bool) => bool = "some"
Expand All @@ -151,8 +150,6 @@ let findIndexOpt = (array: array<'a>, finder: 'a => bool): option<int> =>
| index => Some(index)
}

@new external makeUninitializedUnsafe: int => array<'a> = "Array"
@set external truncateToLengthUnsafe: (array<'a>, int) => unit = "length"
let swapUnsafe = (xs, i, j) => {
let tmp = getUnsafe(xs, i)
setUnsafe(xs, i, getUnsafe(xs, j))
Expand Down Expand Up @@ -200,7 +197,22 @@ let filterMapU = (a, f) => {

let filterMap = (a, f) => filterMapU(a, (. a) => f(a))

// TODO: Change this implementation?
let flatMap = (a, f) => []->concatMany(map(a, f))
let keepSome = filterMap(_, x => x)

@send external flatMap: (array<'a>, 'a => array<'b>) => array<'b> = "flatMap"

let findMap = (arr, f) => {
let rec loop = i =>
if i == arr->length {
None
} else {
switch f(getUnsafe(arr, i)) {
| None => loop(i + 1)
| Some(_) as r => r
}
}

loop(0)
}

@send external at: (array<'a>, int) => option<'a> = "at"
87 changes: 75 additions & 12 deletions src/Core__Array.resi
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@
external fromArrayLikeWithMap: (Js.Array2.array_like<'a>, 'a => 'b) => array<'b> = "Array.from"
@val external fromIterator: Core__Iterator.t<'a> => array<'a> = "Array.from"
@val external fromIteratorWithMap: (Core__Iterator.t<'a>, 'a => 'b) => array<'b> = "Array.from"

/**
`make(~length, init)`

Creates an array of length `length` initialized with the value of `init`.

```res example
Array.make(~length=3, #apple) == [#apple, #apple, #apple]
```
*/
let make: (~length: int, 'a) => array<'a>

/**
`fromInitializer(~length, f)`

Creates an array of length `length` initialized with the value returned from `f ` for each index.

```res example
Array.make(~length=3, i => i + 3) == [3, 4, 5]
```
*/
let fromInitializer: (~length: int, int => 'a) => array<'a>

@val external isArray: 'a => bool = "Array.isArray"
@get external length: array<'a> => int = "length"
@send external copyAllWithin: (array<'a>, ~target: int) => array<'a> = "copyWithin"
Expand Down Expand Up @@ -57,37 +80,54 @@ let lastIndexOfOpt: (array<'a>, 'a) => option<int>
@send external mapWithIndex: (array<'a>, ('a, int) => 'b) => array<'b> = "map"

/**
`reduce(xs, init, f)`
`reduce(xs, f, init)`

Applies `f` to each element of `xs` from beginning to end. Function `f` has two parameters: the item from the list and an “accumulator”; which starts with a value of `init`. `reduce` returns the final value of the accumulator.

```res example
Array.reduce([2, 3, 4], 1, (a, b) => a + b) == 10
Array.reduce([2, 3, 4], (a, b) => a + b, 1) == 10

Array.reduce(["a", "b", "c", "d"], (a, b) => a ++ b, "") == "abcd"
```
*/
@send
external reduce: (array<'b>, ('a, 'b) => 'a, 'a) => 'a = "reduce"

/**
`reduceWithIndex(xs, f, init)`

Applies `f` to each element of `xs` from beginning to end. Function `f` has three parameters: the item from the array and an “accumulator”, which starts with a value of `init` and the index of each element. `reduceWithIndex` returns the final value of the accumulator.

Array.reduce(["a", "b", "c", "d"], "", (a, b) => a ++ b) == "abcd"
```res example
Array.reduceWithIndex([1, 2, 3, 4], (acc, x, i) => acc + x + i, 0) == 16
```
*/
let reduce: (array<'b>, 'a, ('a, 'b) => 'a) => 'a
@send
external reduceWithIndex: (array<'b>, ('a, 'b, int) => 'a, 'a) => 'a = "reduce"

/**
`reduceReverse(xs, init, f)`
`reduceRight(xs, f, init)`

Works like `Array.reduce`; except that function `f` is applied to each item of `xs` from the last back to the first.

```res example
Array.reduceReverse(["a", "b", "c", "d"], "", (a, b) => a ++ b) == "dcba"
Array.reduceRight(["a", "b", "c", "d"], (a, b) => a ++ b, "") == "dcba"
```
*/
let reduceReverse: (array<'b>, 'a, ('a, 'b) => 'a) => 'a
@send
external reduceRight: (array<'b>, ('a, 'b) => 'a, 'a) => 'a = "reduceRight"

/**
Applies `f` to each element of `xs` from beginning to end. Function `f` has three parameters: the item from the array and an “accumulator”, which starts with a value of `init` and the index of each element. `reduceWithIndex` returns the final value of the accumulator.
`reduceRightWithIndex(xs, f, init)`

Like `reduceRight`, but with an additional index argument on the callback function.

```res example
Array.reduceWithIndex([1, 2, 3, 4], 0, (acc, x, i) => acc + x + i) == 16
Array.reduceRightWithIndex([1, 2, 3, 4], (acc, x, i) => acc + x + i, 0) == 16
```
*/
let reduceWithIndex: (array<'a>, 'b, ('b, 'a, int) => 'b) => 'b
@send
external reduceRightWithIndex: (array<'b>, ('a, 'b, int) => 'a, 'a) => 'a = "reduceRight"

@send external some: (array<'a>, 'a => bool) => bool = "some"
@send external someWithIndex: (array<'a>, ('a, int) => bool) => bool = "some"
Expand All @@ -101,11 +141,34 @@ external setUnsafe: (array<'a>, int, 'a) => unit = "%array_unsafe_set"
let findIndexOpt: (array<'a>, 'a => bool) => option<int>
let reverse: array<'a> => array<'a>
let filterMap: (array<'a>, 'a => option<'b>) => array<'b>

/**
`fromInitializer(~length, f)`

Creates an array of length `length` initialized with the value returned from `f ` for each index.

```res example
Array.make(~length=3, i => i + 3) == [3, 4, 5]
```
*/
let keepSome: array<option<'a>> => array<'a>
let shuffle: array<'a> => array<'a>
let shuffleInPlace: array<'a> => unit
let flatMap: (array<'a>, 'a => array<'b>) => array<'b>
@send external flatMap: (array<'a>, 'a => array<'b>) => array<'b> = "flatMap"

/**
`findMap(arr, f)`

Calls `f` for each element and returns the first value from `f` that is `Some(_)`.
Otherwise returns `None`

/**
```res example
Array.findMap([1, 2, 3], n => mod(n, 2) ? Some(n - 2) : None) == 0
```
*/
let findMap: (array<'a>, 'a => option<'b>) => option<'b>

/**
`at(array, index)`

Get an element by its index. Negative indices count backwards from the last item.
Expand Down
Loading