From ee6b0b86a95a3d15f99a817352b5317d44be9b55 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Thu, 11 Sep 2025 14:55:23 +0200 Subject: [PATCH 1/2] Add filterMapWithIndex to Stdlib --- packages/@rescript/runtime/Stdlib_Array.res | 17 +++++++++++++++++ packages/@rescript/runtime/Stdlib_Array.resi | 18 ++++++++++++++++++ .../@rescript/runtime/lib/es6/Stdlib_Array.js | 18 ++++++++++++++++++ .../@rescript/runtime/lib/js/Stdlib_Array.js | 18 ++++++++++++++++++ .../tests/src/expected/Completion.res.txt | 6 ++++++ .../src/expected/DotPipeCompletionSpec.res.txt | 12 ++++++++++++ 6 files changed, 89 insertions(+) diff --git a/packages/@rescript/runtime/Stdlib_Array.res b/packages/@rescript/runtime/Stdlib_Array.res index b73ecd0587..f3c1a42a28 100644 --- a/packages/@rescript/runtime/Stdlib_Array.res +++ b/packages/@rescript/runtime/Stdlib_Array.res @@ -273,6 +273,23 @@ let filterMap = (a, f) => { let keepSome = filterMap(_, x => x) +let filterMapWithIndex = (a, f) => { + let l = length(a) + let r = makeUninitializedUnsafe(l) + let j = ref(0) + for i in 0 to l - 1 { + let v = getUnsafe(a, i) + switch f(v, i) { + | None => () + | Some(v) => + setUnsafe(r, j.contents, v) + j.contents = j.contents + 1 + } + } + truncateToLengthUnsafe(r, j.contents) + r +} + @send external flatMap: (array<'a>, 'a => array<'b>) => array<'b> = "flatMap" @send external flatMapWithIndex: (array<'a>, ('a, int) => array<'b>) => array<'b> = "flatMap" diff --git a/packages/@rescript/runtime/Stdlib_Array.resi b/packages/@rescript/runtime/Stdlib_Array.resi index 0a52fcc888..b194e89f2d 100644 --- a/packages/@rescript/runtime/Stdlib_Array.resi +++ b/packages/@rescript/runtime/Stdlib_Array.resi @@ -1193,6 +1193,24 @@ Array.filterMap([], n => mod(n, 2) == 0 ? Some(n * n) : None) == [] */ let filterMap: (array<'a>, 'a => option<'b>) => array<'b> +/** +`filterMapWithIndex(array, fn)` + +Calls `fn` for each element and returns a new array containing results of the `fn` calls which are not `None`. + +## Examples + +```rescript +["Hello", "Hi", "Good bye"]->Array.filterMapWithIndex((item, index) => + switch item { + | "Hello" => Some(index) + | _ => None + } +) == [0] +``` +*/ +let filterMapWithIndex: (array<'a>, ('a, int) => option<'b>) => array<'b> + /** `keepSome(arr)` diff --git a/packages/@rescript/runtime/lib/es6/Stdlib_Array.js b/packages/@rescript/runtime/lib/es6/Stdlib_Array.js index f733d81a00..2cf6e3066b 100644 --- a/packages/@rescript/runtime/lib/es6/Stdlib_Array.js +++ b/packages/@rescript/runtime/lib/es6/Stdlib_Array.js @@ -162,6 +162,23 @@ function keepSome(__x) { return filterMap(__x, x => x); } +function filterMapWithIndex(a, f) { + let l = a.length; + let r = new Array(l); + let j = 0; + for (let i = 0; i < l; ++i) { + let v = a[i]; + let v$1 = f(v, i); + if (v$1 !== undefined) { + r[j] = Primitive_option.valFromOption(v$1); + j = j + 1 | 0; + } + + } + r.length = j; + return r; +} + function findMap(arr, f) { let _i = 0; while (true) { @@ -197,6 +214,7 @@ export { findIndexOpt, findLastIndexOpt, filterMap, + filterMapWithIndex, keepSome, toShuffled, shuffle, diff --git a/packages/@rescript/runtime/lib/js/Stdlib_Array.js b/packages/@rescript/runtime/lib/js/Stdlib_Array.js index e62a6efb4e..8ba24ecfdb 100644 --- a/packages/@rescript/runtime/lib/js/Stdlib_Array.js +++ b/packages/@rescript/runtime/lib/js/Stdlib_Array.js @@ -162,6 +162,23 @@ function keepSome(__x) { return filterMap(__x, x => x); } +function filterMapWithIndex(a, f) { + let l = a.length; + let r = new Array(l); + let j = 0; + for (let i = 0; i < l; ++i) { + let v = a[i]; + let v$1 = f(v, i); + if (v$1 !== undefined) { + r[j] = Primitive_option.valFromOption(v$1); + j = j + 1 | 0; + } + + } + r.length = j; + return r; +} + function findMap(arr, f) { let _i = 0; while (true) { @@ -196,6 +213,7 @@ exports.reduceRightWithIndex = reduceRightWithIndex; exports.findIndexOpt = findIndexOpt; exports.findLastIndexOpt = findLastIndexOpt; exports.filterMap = filterMap; +exports.filterMapWithIndex = filterMapWithIndex; exports.keepSome = keepSome; exports.toShuffled = toShuffled; exports.shuffle = shuffle; diff --git a/tests/analysis_tests/tests/src/expected/Completion.res.txt b/tests/analysis_tests/tests/src/expected/Completion.res.txt index 85b675f957..0e525703b2 100644 --- a/tests/analysis_tests/tests/src/expected/Completion.res.txt +++ b/tests/analysis_tests/tests/src/expected/Completion.res.txt @@ -238,6 +238,12 @@ Path Array. "tags": [], "detail": "(array<'a>, ('a, 'a) => Ordering.t) => unit", "documentation": {"kind": "markdown", "value": "\n`sort(array, comparator)` sorts `array` in-place using the `comparator` function.\n\nBeware this will *mutate* the array.\n\nSee [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) on MDN.\n\n## Examples\n\n```rescript\nlet array = [3, 2, 1]\narray->Array.sort((a, b) => float(a - b))\narray == [1, 2, 3]\n```\n"} + }, { + "label": "filterMapWithIndex", + "kind": 12, + "tags": [], + "detail": "(array<'a>, ('a, int) => option<'b>) => array<'b>", + "documentation": {"kind": "markdown", "value": "\n`filterMapWithIndex(array, fn)`\n\nCalls `fn` for each element and returns a new array containing results of the `fn` calls which are not `None`.\n\n## Examples\n\n```rescript\n[\"Hello\", \"Hi\", \"Good bye\"]->Array.filterMapWithIndex((item, index) =>\n switch item {\n | \"Hello\" => Some(index)\n | _ => None\n }\n) == [0]\n```\n"} }, { "label": "length", "kind": 12, diff --git a/tests/analysis_tests/tests/src/expected/DotPipeCompletionSpec.res.txt b/tests/analysis_tests/tests/src/expected/DotPipeCompletionSpec.res.txt index f9947b67ff..27187512fb 100644 --- a/tests/analysis_tests/tests/src/expected/DotPipeCompletionSpec.res.txt +++ b/tests/analysis_tests/tests/src/expected/DotPipeCompletionSpec.res.txt @@ -374,6 +374,18 @@ Path filt "range": {"start": {"line": 86, "character": 34}, "end": {"line": 86, "character": 35}}, "newText": "" }] + }, { + "label": "->Array.filterMapWithIndex", + "kind": 12, + "tags": [], + "detail": "(array<'a>, ('a, int) => option<'b>) => array<'b>", + "documentation": {"kind": "markdown", "value": "\n`filterMapWithIndex(array, fn)`\n\nCalls `fn` for each element and returns a new array containing results of the `fn` calls which are not `None`.\n\n## Examples\n\n```rescript\n[\"Hello\", \"Hi\", \"Good bye\"]->Array.filterMapWithIndex((item, index) =>\n switch item {\n | \"Hello\" => Some(index)\n | _ => None\n }\n) == [0]\n```\n"}, + "sortText": "filterMapWithIndex", + "insertText": "->Array.filterMapWithIndex", + "additionalTextEdits": [{ + "range": {"start": {"line": 86, "character": 34}, "end": {"line": 86, "character": 35}}, + "newText": "" + }] }, { "label": "->Array.filter", "kind": 12, From 732998f003b9bb22c57750054f57df54e9f95856 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Thu, 11 Sep 2025 15:27:49 +0200 Subject: [PATCH 2/2] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 031cc623b6..ff766376d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ #### :rocket: New Feature +- Add `Array.filterMapWithIndex` to Stdlib. https://github.com/rescript-lang/rescript/pull/7876 + #### :bug: Bug fix - Fix code generation for emojis in polyvars and labels. https://github.com/rescript-lang/rescript/pull/7853