Optional
depthOptional
strictIf true
, only isMany
input values (according to z.isMany
or option.allProps
) are allowed and iterated over (excluding WeakMap
& WeakSet
which can't be iterated at all). All other z.isSingle
values (& WeakMap
& WeakSet
) throw an error.
If it is false (default), single & Weak inputs are "iterated over" as a single item iteration. All isSingle
input values are iterated as-is, even falsey values, which means you can safely use them in a for...of loop()
, on top of any code block that operates on isSingle
or isMany
values!
All isMany
input values are iterated over props/indexes/items always the same way, regardless of this option.
Optional
loopIf true
(default), isSingleOrWeak
inputs are iterated as a single item iteration (of the input value itself).
If false
, input values are omitted from the "iteration", i.e they are not iterated at all, a 0-item loop.
A soft alternative to strict: true
that doesn't throw, but just omits the single values.
Optional
loop@todo: false NOT IMPLEMENTED YET
If true
(default), iterating over iterators/generators also receive a count
parameter: the number of items that have been emitted so far.
You can turn it off, if your iterator might exceed the number limit & and the small overhead of counting.
See count
parameter in TitemsCallback
Optional
async@todo: true NOT IMPLEMENTED yet - treats Promises as normal items. Till then you can await it: for (const [item, idx] of z.loop(promises) console.log(await item, idx)
If false
(default), it treats nested items that are Promises, as normal items (i.e `for [item] of loop(promises) { item instanceOf Promise === true}
If true
, it awaits each isPromise
item and passes the resolved value as the item in the loop. It returns an AsyncGenerator
, so the for...of
must be awaited, for example:
const promises = [ Promise.resolve(1]), Promise.resolve(2]), Promise.resolve(3]), ]
for await (const [item, idx, count] of z.loop(promises, {asyncUnwrap: true, filter: oddNumbers})) {
item instanceOf Promise === false
_.isNumber(item) === true
console.log({item, idx, count})
}
// {item: 1, idx: 0, count: 1
// {item: 3, idx: 2, count: 2
Note: if input value is a Promise itself, it is NEVER awaited, but treated as an `isSingle` value. All you need to do is to `loop(await inputPromise)` though
Optional
asyncAsync option: NOTE: it applies only when looping with z.each()
, not the normal _.zloop()
If Async is enabled, then if iteratee
callback returns a promise, it waits for it to resolve before continuing to the next element(s) & the each()
call returns a Promise<input>
, resolved when iteration is over.
Async is enabled in two ways: Auto Async & Forced Async
async: undefined
(default), async is "Auto Async": it goes into async mode automatically, if the iteratee is a real-runtime async function. // Auto Async mode, as iteratee is async
z.each(persons, async (person, idxKey) => registerPersonPromise(person))
// waits until each person is registered, before continuing to the next.
but
// No async mode, as iteratee is not marked as async (not it returns a Promise)
z.each(persons, (person, idxKey) => {
registerPersonPromise(person)
return null
})
// DOES NOT wait until each person is registered, before continuing to the next. Effectively, all promises are dispatched simultaneously.
Note: there's a discrepancy between TypeScript's static evaluation of async
functions and the actual runtime behavior. TypeScript will always treat as async
function, any function determined to be returning a Promise
, even if it is not marked as async function(...){}
. For example:
// Our iteratee is an implicit async function, in the TypeScript world, because the iteratee is returning a promise. Causes a discrepancy between TypeScript static compilation & JS runtime:
z.each(persons, (person, idxKey) => registerPersonPromise(person) )
// DOES NOT wait until each person is registered, before continuing to the next. Effectively, all promises are dispatched simultaneously.
As iteratee is not a real async function
, we have no way of knowing to enable the Auto Async mode at runtime. But TypeScript reports it as a Promise returning function, hence at compile type it is treated as async. In short:
Promise<typeof input>
as each()
resulteach()
returns plain input
).This can't be overcome AFAIK, SIJS (Such Is JavaScript). Just mark you promise-returning functions with async
keyword, if you want to use Auto Async mode, or use async: true
option below.
async: true
)async: true
, it is forced into async mode: it awaits on each callback return (if its a promise), before continuing to the next (and each()
returns a Promise<input>
). The each()
call returns a Promise<input>
, resolved when iteration is over. z.each(persons, (person, idxKey) => registerPersonPromise(person), {async: true})
// waits until each person is registered, before continuing to the next.
async: number
, it is forced into async mode with given N parallelism: it awaits on each callback return, but processes up to the number of N tasks in parallel, before continuing to a next item (and each()
returns a Promise<input>
).
Note: async: true
is equivalent to async: 1
, i.e parallelism of 1 (see bellow).
Warning: items will be processed in a non-stable order (cause of unpredictable Promises resolving).
@todo: NOT IMPLEMENTED (any number is treated as true) z.each(persons, (person, idxKey) => registerPersonPromise(person), {async: 3})
// waits until each person is registered, up to 3 persons at a time, before continuing to a next person.
async: false
, it is forced into sync mode (even for real async functions). It does not await on each callback return, before continuing to the next (and each()
returns the original input
right away).Note: if input value is a Promise itself, it is NEVER awaited, but treated as an isSingle
value. All you need to do is to loop(await inputPromise)
though!
Optional
filterA filter
callback, used to filter out items as they are iterated over.
If both filter
and map
are provided, filter
is applied first, then map
is applied to the result.
WARNING: If you return STOP
from the filter callback, the loop will stop immediately, without calling the map
callback.
WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 })
, it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop()
parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 })
if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks
Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop()
signature detection will fail!
Optional
mapA mapping callback (projection), used to map the items as they are iterated over.
If both filter
and map
are provided, filter
is applied first, then map
is applied to the result.
WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 })
, it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop()
parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 })
if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks
Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop()
signature detection will fail! *
Optional
map@todo: NOT IMPLEMENTED/tested yet
A mapping function that can be used to map the keys that are iterated over.
If both filter
and map
are provided, filter
is applied first, then mapKeys
is applied to the result.
WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 })
, it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop()
parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 })
if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks
Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop()
signature detection will fail!
Optional
takeIf take
is a number
, it limits the number of items that are iterated over.
If take
is a function
, it stops the iteration the 1st time it returns false
If options.props
& value is an _.isObject()
other than realObject
, it will iterate both on:
n
keys/idx of the value (eg the Array
elements, Set
items etc)n
of props as wellWARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 })
, it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop()
parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 })
if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks
Note: No guaranteed order for property bags like Objects
, Sets
, Maps
etc and return the same value type of input value.
Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop()
signature detection will fail!
Optional
propsIf true
, consider ONLY the props, instead of Nested Keys for special objects (Arrays, TypedArrays, Maps & Sets) - mimics Object.keys()
/ _.keys()
if so.
By default, z.keys()
returns the "natural keys" for special objects with nested values:
Array
& TypedArray
(eg [0, 1, 2, 3, ...]
)Map
& Set
(eg ['someMapKey', {anObjectUsedAsKey: true}, Symbol('aSymbolKey')]
instead of their Object props.Similarly, all other functions like loop()
& map()
(that extend these options), iterate on those "natural keys" for all of these special objects.
For all other objects (including realObject
, all Iterators & Generators, Boxed Primitives etc),
string
/symbol
props are returned in keys()
loop()
and family, also depending on their other function-specific options.If props: true
then the keys()
& iterations are only against props, even for special objects, instead of Nested Keys.
This only affects the special objects: it makes no difference to non-special values, if props
is true
/false
, as their props are always considered anyway.
Note that Object.keys()
always returns props for ALL objects (i.e props: true
always), and there is no way to grab natural keys (or symbol props for that matter). This is a special feature of z.keys()
, and much more useful: why would you want the props of Map
or an Array
anyway, 99% of the time? But if you subclassed Array
or Map
and want to get the props of that instance (instead of their natural keys), then you use props: true
.
Finally, if props: 'all'
, you get both the props (first), followed by Nested Keys. Useful for print/debugging, comparisons etc.
@default: false / undefined
Optional
stringWhether to include normal string props
@default: true
Optional
symbolWhether to include Symbol props, when we're dealing with a realObject
or props: true
@default: false
Optional
ownWhether to include own object props.
Note: It refers to props only, not Nested Keys, for arrays specifically. We include naturalKeys (i.e array Indexes) even when { own: false, props: 'all' }
, to keep consistent with other Inspectable Nested Keys holders (Map & Set). But in realObjects, { own: false, props: any }
will return no own props, as it should.
@default: true
Optional
inheritedIf true, it walks the prototype chain & retrieves all keys of inherited objects.
@default: false
Optional
topIf true (and nonEnumerables
is also true), it includes top-level (i.e Object
) props like 'constructor', 'toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString'
etc.
It also retrieves these hidden props (that have no types definitions): `'defineGetter', 'defineSetter', lookupGetter_', 'lookupSetter', 'proto'.
@default: false
Optional
hiddenIf hidden: true
(and nonEnumerables
& top
are also true), it includes top-level (i.e Object
) hidden props like `'defineGetter', 'defineSetter', lookupGetter_', 'lookupSetter', 'proto'. Note that these are hidden props, thus have no TypeScript definitions.
Also, if hidden: true
, it un-hides user/custom props that start & end with __
, eg __myHiddenProp__
(which are hidden otherwise by default) @todo(363): filter user/custom __hidden__
props in KeysT/Values types in typescript
Note: hidden
is a.k.a as private
in the JS/TS world, but we use hidden
to avoid confusion with the private
keyword in TS and possible different semantics. Will revisit in future versions.
@default: false
Optional
enumerablesWhether to include enumerable keys
Optional
nonWhether to include enumerable keys
Optional
dataWhen you loop()
over an ArrayBuffer
(or request keys()
or values()
), you must provide the dataViewType
option (eg Int16
, Float32
etc). The type of the items in the ArrayBuffer
is required by the internal DataView
that reads/writes to the ArrayBuffer.
Throws if dataViewType
is missing and z.type(input) === 'ArrayBuffer'
Optional
sparseIf true
, it respects the position of Array items, assigning the mapped/filtered array items to the same indexes (i.e sparse Arrays, will stay sparse). If false
, it will always return a dense/compact Array.
@default: false
Optional
filterIf filterSingles: false
(default), it throws an error on any z.isSingleOrWeak
input (eg z.filter(123)
throws), as well as for WeakMap
& WeakSet
which are treated as singles (they can't be iterated over).
Otherwise, if filterSingles: true
see singlesReject
option for explanation.
Optional
singlesIf filterSingles: false
(default), it throws an error on any single input (eg z.filter(123)
throws).
BUT:
If filterSingles: true
, it returns a "clone" of the single value itself, if it passes the filterCb!
So, if filterCb
passes:
Primitives
are returned as isBoxedPrimitive
instances & Boxed single values (eg Date, RegExp) are returned a new instance of the same type, with same underlying value. This is so it's consistent with the other collection functions (map, clone etc), which return new instances of the same type.Now, if filterCb
doesn't pass, the return value depends on singlesReject
option:
If singlesReject
is truthy, it returns that value. Note that the default is the special value z.NOTHING
(which equals to Symbol('z.NOTHING')
)
If singlesReject
is falsey, it returns what ever this value is (eg undefined
) in most cases, except:
number
becomes NaN
BigInt
also becomes NaN
Symbol
becomes z.NOTHING
(i.e Symbol('z.NOTHING')
)Date
becomes new Date(null)
, which is 1970-01-01T00:00:00.000ZFunction
becomes a () => EMPTY
(i.e () => Symbol('z.NOTHING')
)Boxed Primitives (please, please DONT use them!) are returned as new instances of the same type, with an "invalid" value:
Number
becomes new Number(NaN)
Boolean
becomes new Boolean(false) (which is bad!), but who uses Boolean instances anyway?String
becomes new String('')
- also bad to use!Note: All other _.isObject
single input values (eg Date
, RegExp
, BoxedPrimitive
instances) are returned as a new instance of same type, even if filterCb
rejects them.
@default: z.NOTHING (i.e Symbol('z.NOTHING')
)
Options interface for
z.isRefDisjoint