Function map

  • 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() or Array.map() or _.mapValues(), but powered by z.loop(). Hence:

    • it works with many value types, not just arrays (and objects with separate _.mapValues()) but also on Map, Set, Iterator, Generator, AsyncIterator, Function, Promise and even isPrimitive / isSingles!

    • the main twist: it is not returning always just an Array like the normal _.map or Array.map(), but a copy of whatever the input value was, with the nested items mapped/projected over via the mapCb function. So a map(new Map([])) will return a new Map([]), with the same keys & mapped values of the input Map values. A map({}) 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 in map().

    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 with symbol: true), unlike _.mapValues() which deals only with string keys. But access is prohibited from inherited and nonEnumerables 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 except next, Symbol.iterator & Symbol.asyncIterator (since these would mess the iteration). Also in strict:true, you can't use symbol: true (via props) with Iterators/Generators (it throws).

    • z.isSingle values (primitives & boxed) are also supported: the projection callback will be called only once (by default, with options.loopSingles: true & options.strict: false) or throw an error (with options.strict: true). This follows the functional programming principles, that all values are enclosed and can be mapped over. This is also the reason why options.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, eg const numAsString = map(42, (v) => v + '') will return '42' as a string, not 42 as number (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, with props:true - ignored for non-objects. WARNING: it is dangerous to copy inherited props like methods that might be bound or internals like Symbol.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) via IloopOptions.

    • 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).

      • NOTE: if options.map already exists on map(val, myOptions, myMapCb), it throws an error.
      • But if 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 with sparse: false in the options.

    Type Parameters

    Parameters

    Returns GetMapReturn<Tinput, Toptions, ImapCallback, null>

    the 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.