UNPKG

stick-js

Version:

Fast toolkit for functional programming in JS. Provides idioms for referentially transparent expressions, clear separation of mutable and immutable operations, object factories, function calls based on English grammar, and pipe & compose operators.

51 lines 4.59 kB
/* * These functions are for mapping objects. * * To map arrays, functors or any other data type which has its own `map` method, use our `map` * function, which dispatches to the prototype `map`, but capped. * * An object mapped using one of the `map` functions always results in a new object. The names * `mapKeys`, `mapValues`, and `mapTuples` should make it clear how the mapping happens. * * An object mapped using one of the `remap` functions results in an array. The name `remap` is * meant to make it clear that the shape changes. The `remap` functions follow the same form as the * `map` functions. * * We map only over own, enumerable properties. * * Since arrays are objects in JS, `(re)mapKeys`, `(re)mapValues` and `(re)mapTuples` can also be * used on arrays. The latter is useful when you start with an array and want to end up with an * object. See `fromPairs` for an example of this. * * The WithFilter versions are exported so that map.js can create the mappings, but not further * exported after that. * * Filters work on truthiness, and they take the mapped value as input. * * Map + filter is a convenience for avoiding `reduceObj`. * * It turns out in practice that the 'in' variants, which also loop through non-own properties, are * not that useful for FP in JavaScript, so we don't provide them. * * 1) Inherited properties *tend* to be methods (though not always), so mapping, reducing etc. is * not that useful. * 2) Looping through non-own properties requires `for ... in`, which: * a) doesn't include symbol properties: we consider symbol properties to be useful in FP, in * addition to normal string properties. (`Ramda.keys` doesn't include them either); * b) doesn't produce a guaranteed order. * * Nearly all of the built-in iteration functions in `Object` and `Reflect` only loop through own * properties. * * @todo reduceAs etc. */import{propertyIsEnumerable,safeObject}from"./internal.mjs";const canEnum=(o,k)=>propertyIsEnumerable.call(o,k);export const remapKeys=f=>o=>{const ret=[];for(const k of Reflect.ownKeys(o))if(canEnum(o,k))ret.push(f(k));return ret;};export const _remapKeysWithFilter=p=>f=>o=>{const ret=[];for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const kk=f(k);if(p(kk))ret.push(f(k));}return ret;};export const remapValues=f=>o=>{const ret=[];for(const k of Reflect.ownKeys(o))if(canEnum(o,k))ret.push(f(o[k]));return ret;};export const _remapValuesWithFilter=p=>f=>o=>{const ret=[];for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const kk=f(o[k]);if(p(kk))ret.push(kk);}return ret;};export const remapTuples=f=>o=>{const ret=[];for(const k of Reflect.ownKeys(o))if(canEnum(o,k))ret.push(f(k,o[k]));return ret;};export const _remapTuplesWithFilter=p=>f=>o=>{const ret=[];for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const kkvv=f(k,o[k]);const[kk,vv]=kkvv;if(p(kk,vv))ret.push(kkvv);}return ret;};// --- note: in all functions which map keys or tuples, it's up to you to ensure that the keys don't // clash. // clashing keys will lead to unpredictable behavior between runtimes. export const mapKeys=f=>o=>{const ret=safeObject();for(const k of Reflect.ownKeys(o))if(canEnum(o,k))ret[f(k)]=o[k];return ret;};export const _mapKeysWithFilter=p=>f=>o=>{const ret=safeObject();for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const kk=f(k);if(p(kk))ret[kk]=o[k];}return ret;};export const mapValues=f=>o=>{const ret=safeObject();for(const k of Reflect.ownKeys(o))if(canEnum(o,k))ret[k]=f(o[k]);return ret;};export const _mapValuesWithFilter=p=>f=>o=>{const ret=safeObject();for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const vv=f(o[k]);if(p(vv))ret[k]=vv;}return ret;};/* Map an array or an object to an object. * note: filter is truthy, and works on the mapped value. * note: it is up to the caller to ensure that the resulting keys don't clash. * * @experimental You can also use this to map an array to an object, since arrays are also objects. * Be careful, because the index will be a String, not a Number. * @future make separate function for arrays. */export const mapTuples=f=>o=>{const ret=safeObject();for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const[kk,vv]=f(k,o[k]);ret[kk]=vv;}return ret;};export const _mapTuplesWithFilter=p=>f=>o=>{const ret=safeObject();for(const k of Reflect.ownKeys(o)){if(!canEnum(o,k))continue;const[kk,vv]=f(k,o[k]);if(!p(kk,vv))continue;ret[kk]=vv;}return ret;};export const fromPairs=mapTuples((_,[k,v])=>[k,v]);export const toPairs=remapTuples((k,v)=>[k,v]);