Optional
options: Toptionsthe same type as the input value, but with the items mapped over. Objects, Arrays, Maps, Sets etc are copied and returned as new values. Also Iterators, Generators etc are returned as a Generator with their items mapped over.
z.loop()
the power hidden behind map()
and all other collection functions.
Map over the (many) items of any possible input value, returning a new instance of the same input value type (if possible), with the results of calling the projector/mapping function on every "nested" element of the input value (for collections like Objects, Arrays, Maps, Sets but also for Iterators, Generators, AsyncIterators etc).
Similar idea to lodash
_.map()
orArray.map()
or_.mapValues()
, but powered byz.loop()
. Hence:it works with many value types, not just arrays (and objects with separate
_.mapValues()
) but also onMap
,Set
,Iterator
,Generator
,AsyncIterator
,Function
,Promise
and evenisPrimitive
/isSingle
s!the main twist: it is not returning always just an
Array
like the normal_.map
orArray.map()
, but a copy of whatever the input value was, with the nested items mapped/projected over via themapCb
function. So amap(new Map([]))
will return anew Map([])
, with the same keys & mapped values of the input Map values. Amap({})
will return a new object, with the same props as the original object, but with the values mapped over. And so on!Using
props: true
orprops: 'all'
you can only/also copy over the props of the original object, just like with_.mapValues()
, but works for all object types. But this will use ONLY enumerable & own props, to avoid messing with the new object's behavior and internals, and since these already exist on the new instance. Hence,inherited: true
&nonEnumerables: true
are ignored inmap()
.In effect:
Arrays are copied & mapped verbatim. Sparse Arrays will be mapped compacted / dense by default: the empty items will disappear in the new array, the new array will become dense. But if
sparse: true
it will be respecting sparse items position, and the new array will be sparse as well.Objects are copied over like with
_.mapValues()
, respecting their props, including symbols ones (optionally withsymbol: true
), unlike_.mapValues()
which deals only with string keys. But access is prohibited frominherited
andnonEnumerables
props.Maps & Sets are also copied over to new Map & Set, with their items mapped over.
Functions are also supported, and the result is a new function, that passes the args & this to original function, and returns the return value of the original value, but mapped.
Promises are similar to functions: a new Promise is returned, that resolves to the mapped value of the original promise.
Boxed primitives become a new instance of the same type, with their boxed value mapped.
User defined classes are NOT supported, it throws, cause it makes no sense.
// @todo: 2 options:
Iterators
/Generator
sync or async:are NOT supported, because they can't be iterated over without affecting the value, and they are potentially infinite ! The result is a new
Generator
/AsyncGenerator
is the mapped items of the original.are also supported, but use with caution! The result is a new
Generator
/AsyncGenerator
with the mapped items of the original.NOTE: it uses ONLY the remaining
Iterator
/Generator
items, since there is no contract to restart Iterators in JS at the moment.NOTE: When
props: 'all'
, the Generator's props are also copied over to the new Generator, all exceptnext
,Symbol.iterator
&Symbol.asyncIterator
(since these would mess the iteration). Also in strict:true, you can't usesymbol: true
(viaprops
) with Iterators/Generators (it throws).z.isSingle
values (primitives & boxed) are also supported: the projection callback will be called only once (by default, withoptions.loopSingles: true
&options.strict: false
) or throw an error (withoptions.strict: true
). This follows the functional programming principles, that all values are enclosed and can be mapped over. This is also the reason whyoptions.strict: false
is the default.Primitives are the only exception to the rule of "getting back the same value type": you can change the return mapped type of the
input
, egconst numAsString = map(42, (v) => v + '')
will return'42'
as astring
, not42
asnumber
(and type will follow). It's up to you if you want to return the same input value type!WeakMap, WeakSet are NOT supported & throw, since they can't be iterated / mapped / cloned.
In all cases, any extra object props (eg
Array
Props,Map
props etc) can be copied over to the new Array, withprops:true
- ignored for non-objects. WARNING: it is dangerous to copy inherited props like methods that might be bound or internals likeSymbol.iterator
to the mapped object, as they might mess with the new object's behavior. Some of these case are coded against, but dragons might still lurk.Powered by the mighty
z.loop()
/z.keys
&z.values
, hence you can control which keys / idx are visited (own
/inherited
/nonEnumerables
/string
/symbol
etc) viaIloopOptions
.You can pass an
IloopOptions
object as the second parameter, instead of the callback, to control which keys / idx are visited (own / inherited / enumerable etc).options.map
already exists onmap(val, myOptions, myMapCb)
, it throws an error.options.filter
exists, it is applied before the map. If the input value is an array, elements will appear on the result mapped array at the same indexes of the original, hence the resulting array will be sparse, for elements that are missing (because they didn't pass the filter). You can change this behavior withsparse: false
in the options.