Open Mistyputt opened 4 years ago
I've played around with a naive implementation:
function mapArrayWithIndex<A, B>(f: (a: A, i: number) => B, array: A[], offset: number): [B[], number] {
const result = new Array(array.length);
for (let i = 0; i < array.length; ++i) {
result[i] = f(array[i], offset + i);
}
return [result, offset + array.length];
}
function mapNodeWithIndex<A, B>(f: (a: A, i: number) => B, node: Node, depth: number, offset: number, adjust: number): [Node, number] {
if (depth !== 0) {
const { array } = node;
var innerOffset = offset;
const result = new Array(array.length);
for (let i = 0; i < array.length; ++i) {
let [res, newOffset] = mapNodeWithIndex(f, array[i], depth - 1, innerOffset, adjust * 32);
innerOffset = newOffset;
result[i] = res
}
return [new Node(node.sizes, result), innerOffset];
} else {
let [res, newOffset] = mapArrayWithIndex(f, node.array, offset);
return [new Node(undefined, res), newOffset];
}
}
function mapPrefixWithIndex<A, B>(f: (a: A, i: number) => B, prefix: A[], length: number): B[] {
const newPrefix = new Array(length);
for (let i = length - 1; 0 <= i; --i) {
newPrefix[i] = f(prefix[i], length - 1 - i);
}
return newPrefix;
}
function mapAffixWithIndex<A, B>(f: (a: A, i: number) => B, suffix: A[], length: number, totalLength: number): B[] {
const priorLength = totalLength - length;
const newSuffix = new Array(length);
for (let i = 0; i < length; ++i) {
newSuffix[i] = f(suffix[i], priorLength + i);
}
return newSuffix;
}
/**
* Applies a function to each element in the given list, and its index, and returns a
* new list of the values that the function return.
*
*/
export function mapWithIndex<A, B>(f: (a: A, i: number) => B, l: List<A>): List<B> {
return new List(
l.bits,
l.offset,
l.length,
mapPrefixWithIndex(f, l.prefix, getPrefixSize(l)),
l.root === undefined ? undefined : mapNodeWithIndex(f, l.root, getDepth(l), getPrefixSize(l), 1)[0],
mapAffixWithIndex(f, l.suffix, getSuffixSize(l), l.length)
);
}
Maybe one could use the depth and branchingFactor instead but this seems performance-wise to already fare better than zipWith. Here are my rudimentary benchmark results:
List.map 10 36818486.05 op/s ± 0.54% (86 samples)
List.mapWithIndex 10 32894014.39 op/s ± 0.39% (88 samples)
List.zipWith 10 4332197.10 op/s ± 0.63% (84 samples)
List.map 30000 14337.09 op/s ± 0.71% (88 samples)
List.mapWithIndex 30000 11587.69 op/s ± 0.54% (88 samples)
List.zipWith 30000 1240.52 op/s ± 0.61% (87 samples)
@Mistyputt I just ran into the same issue where I want to know which position in a row an element was clicked. The row is created using map function. I would like to do the following
L.map((x, i) => <Poster content={x} position={i} />, contentL)
I guess a workaround for now can be to use indexOf
Thanks for opening the issue and providing feedback. I definitely agree that it would be nice to offer an easy way to map with an index.
I think your mapWithIndex
implementation looks great @Mistyputt. Would you like to add it in a PR? 😄
Absolutely!
I did work around a few issues building the project when trying out that implementation and would feel better if I had a "clean" project when submitting the PR. After cloning I ran npm install
, then npm run build
. That failed with errors on ts-toolbelt
and devtoolsFormatters
:
❯ npm run build
> list@2.0.19 build /Users/thomas/git/github/funkia/list3
> npm run build-es6; npm run build-cmjs
> list@2.0.19 build-es6 /Users/thomas/git/github/funkia/list3
> tsc -P ./tsconfig-build.json --outDir 'dist/es' --module es2015
node_modules/@types/ramda/index.d.ts:546:28 - error TS2307: Cannot find module 'ts-toolbelt'.
546 import { A, F, T, O } from "ts-toolbelt";
~~~~~~~~~~~~~
src/devtools.ts:15:8 - error TS2339: Property 'devtoolsFormatters' does not exist on type '(Window & typeof globalThis) | Global'.
Property 'devtoolsFormatters' does not exist on type 'Window & typeof globalThis'.
15 if (gw.devtoolsFormatters === undefined) {
~~~~~~~~~~~~~~~~~~
src/devtools.ts:16:6 - error TS2339: Property 'devtoolsFormatters' does not exist on type '(Window & typeof globalThis) | Global'.
Property 'devtoolsFormatters' does not exist on type 'Window & typeof globalThis'.
16 gw.devtoolsFormatters = [];
~~~~~~~~~~~~~~~~~~
src/devtools.ts:26:4 - error TS2339: Property 'devtoolsFormatters' does not exist on type '(Window & typeof globalThis) | Global'.
Property 'devtoolsFormatters' does not exist on type 'Window & typeof globalThis'.
26 gw.devtoolsFormatters.push({
~~~~~~~~~~~~~~~~~~
Found 4 errors.
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! list@2.0.19 build-es6: `tsc -P ./tsconfig-build.json --outDir 'dist/es' --module es2015`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the list@2.0.19 build-es6 script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/thomas/.npm/_logs/2020-04-30T21_15_23_326Z-debug.log
> list@2.0.19 build-cmjs /Users/thomas/git/github/funkia/list3
> tsc -P ./tsconfig-build.json
src/devtools.ts:15:8 - error TS2339: Property 'devtoolsFormatters' does not exist on type '(Window & typeof globalThis) | Global'.
Property 'devtoolsFormatters' does not exist on type 'Window & typeof globalThis'.
15 if (gw.devtoolsFormatters === undefined) {
~~~~~~~~~~~~~~~~~~
src/devtools.ts:16:6 - error TS2339: Property 'devtoolsFormatters' does not exist on type '(Window & typeof globalThis) | Global'.
Property 'devtoolsFormatters' does not exist on type 'Window & typeof globalThis'.
16 gw.devtoolsFormatters = [];
~~~~~~~~~~~~~~~~~~
src/devtools.ts:26:4 - error TS2339: Property 'devtoolsFormatters' does not exist on type '(Window & typeof globalThis) | Global'.
Property 'devtoolsFormatters' does not exist on type 'Window & typeof globalThis'.
26 gw.devtoolsFormatters.push({
~~~~~~~~~~~~~~~~~~
As no one else has reported this I feel I'm missing something obvious. When I played around with the mapWithIndex implementation I just commented out src/devtools.ts
and ran the build-cmjs only, not the es6 that additionally failed on ts-toolbelt
.
I built with node v10.19.0. Any pointers?
Hi
My use case frequently involves mapping a list of raw data into a UI representation that offers the ability to modify elements. For instance mapping a list into table rows, where the user may modify or delete rows. I was thinking having the index available when mapping would allow me to efficiently update the list on that index.
One could use List.zipWith(f, list, List.range(0,List.length(list))) or avoid the index and use List.find but maybe there is a place for a dedicated mapWithIndex function as that should theoretically be more efficient?
Does anyone else have similar use-cases and what is your solution?
Finally, thanks for a great lib!