https://github.com/damianc/array-x
Set of additional array methods.
https://github.com/damianc/array-x
array array-methods arrays counting data-grouping javascript
Last synced: about 1 year ago
JSON representation
Set of additional array methods.
- Host: GitHub
- URL: https://github.com/damianc/array-x
- Owner: damianc
- Created: 2022-09-03T15:07:38.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2022-11-25T18:44:05.000Z (over 3 years ago)
- Last Synced: 2025-01-26T02:19:55.809Z (about 1 year ago)
- Topics: array, array-methods, arrays, counting, data-grouping, javascript
- Language: JavaScript
- Homepage:
- Size: 300 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# `array-x`
## Installation
```
npm i @damianc/array-x
```
> Note that it's `@damianc/array-x`: if you only type `array-x`, completely other package will be installed!
Observing:
- [`observe()`](#observe)
Collecting:
- [`group()`](#grouplabelFactory)
- [`index()`](#indexkeySelector-valueSelector--null-fallbackKey--__unindexed)
- [`pivot()`](#pivotindexKey--null-valueSelector--null-keyMapper--null)
- [`joinOneToOne()`](#joinOneToOnesource-targetKey-sourceKey-targetNewKey-sourceValueSelector--null)
- [`joinOneToMany()`](#joinOneToManysource-targetKey-sourceKey-targetNewKey-sourceValueSelector--null)
- [`joinManyToOne()`](#joinManyToOnesource-targetKey-sourceKey-targetNewKey-sourceValueSelector--null)
- [`takeUntil()`](#takeUntilitemOrMatcher-inclusive--true)
- [`takeUntilReduce()`](#takeUntilReduceaccTester-reducer-reducerInit-inclusive--true)
- [`skipUntil()`](#skipUntilitemOrMatcher-inclusive--true)
- [`skipUntilReduce()`](#skipUntilReduceaccTester-reducer-reducerInit-inclusive--true)
- [`extractSub()`](#extractSubpattern-multi--true-overlay--true)
- [`uniq()`](#uniqselector)
- [`uniqAdjacent()`](#uniqAdjacentselector)
- [`nth()`](#nthindex)
- [`multiple()`](#multipleindexes)
- [`last()`](#last)
- [`pluck()`](#pluckpath)
- [`pluckMultiple()`](#pluckMultiplepaths)
- [`glue()`](#glueotherArray---headSize-tailSize--headSize)
- [`glueHeads()`](#glueHeadsotherArray---leftSize-rightSize--leftSize)
- [`glueTails()`](#glueTailsotherArray---leftSize-rightSize--leftSize)
- [`iterator()`](#iterator)
- [`revIterator()`](#revIterator)
- [`refIterator()`](#refIteratorrefKey-nextKey-initItemIdx--0)
- [`cyclicIterator()`](#cyclicIterator)
- [`echoIterator()`](#echoIteratorsticky--false)
- [`everyNth()`](#everyNthn--1-from--0-to)
- [`select()`](#selectfrom--0-to---1-step--1)
Searching:
- [`localize()`](#localizeitemOrMatcher)
- [`localizeAll()`](#localizeAllitemOrMatcher)
- [`localizeMin()`](#localizeMinmapper)
- [`localizeMax()`](#localizeMaxmapper)
Examining:
- [`count()`](#countitemOrMatcher)
- [`countAll()`](#countAlllabelFactory)
- [`avg()`](#avgmapper)
- [`wavg()`](#wavgweightsArrOrWeightSelector-selector)
- [`sum()`](#summapper)
Clustering:
- [`chunk()`](#chunksize-rejectStickingTail--false)
- [`chunkByCallback()`](#chunkByCallbackcb-matchedItemOpening--true)
- [`chunkByReduce()`](#chunkByReducereducer-init--null-tester--null)
- [`chunkByPattern()`](#chunkByPatternsizes-rejectStickingTail--false)
- [`chunkByGroup()`](#chunkByGroupgrouper--item--0)
- [`partition()`](#partitionpartitioner--item--0)
- [`chunkReduce()`](#chunkReducesize-reducer--null-init--null-rejectStickingTail--false)
- [`frameReduce()`](#frameReducesize-reducer--null-init--null-rejectStickingTail--false)
- [`zip()`](#zipotherArrays)
- [`zipAll()`](#zipAllotherArrays)
- [`unzip()`](#unzip)
- [`unzipAll()`](#unzipAll)
- [`split()`](#splitmatcher--null)
- [`splitByPattern()`](#splitByPatterntake-skip-from--null-rejectStickingTail--false)
- [`cut()`](#cutindex)
- [`chop()`](#chopcuts)
Altering:
- [`wrapAlter()`](#wrapAlterprocessor)
- [`adapt()`](#adaptotherArray---adaptations--1)
- [`adaptByCallback()`](#adaptByCallbackotherArray---tester--null-adaptations--1-lazy--false)
- [`random()`](#randomcount--1)
- [`shuffle()`](#shuffle)
- [`move()`](#movesourceIndex-count-targetIndex--0)
- [`insert()`](#insertindex-items)
- [`overwrite()`](#overwriteindex-items)
- [`override()`](#overrideindex-items)
- [`cork()`](#corkvalue)
- [`precede()`](#precedeappendix---fillWithUndefined--false)
- [`follow()`](#followappendix---fillWithUndefined--false)
- [`frozen()`](#frozen)
- [`fixed()`](#fixed)
- [`dwarf()`](#dwarf)
- [`alterable()`](#alterable)
- [`clamped()`](#clampedmin-max)
- [`fold()/unfold()`](#foldcoverSelectorunfoldindex)
- [`sparse()`](#sparseslots--1)
- [`thick()`](#thick)
Sets:
- [`union()`](#unionotherSet--)
- [`intersection()`](#intersectionotherSet--)
- [`diff()`](#diffotherSet--)
- [`symDiff()`](#symDiffotherSet--)
- [`complement()`](#complementotherSet--)
Redefined built-ins:
- [`reduce()`](#reducereducer-finalizer--null-init)
- [`scan()`](#scanreducer-init--null)
- [`sample()`](#samplesize-step--1)
- [`flat()`](#flatlevel---1)
- [`sort()`](#sortcomparators)
- [`reversed()`](#reversed)
- [`forEach()`](#forEachcb)
- [`spreadMap()`](#spreadMapmapper)
- [`filterMapped()`](#filterMappedmapper--null-filter--null)
- [`replace()`](#replacematcher--x--x-replacer--x--x)
- [`findIndexes()`](#findIndexesmatcher--null)
- [`includes()`](#includesmatcher--null)
- [`mapReduce()`](#mapReducechunkSize--2-reducer--null-init--null-rejectStickingTail--false)
- [`pop()`](#popn--1)
- [`shift()`](#shiftn--1)
- [`pull()`](#pullindex)
- [`remove()`](#removevaluesOrCb)
- [`fill()`](#fillvalueOrGenerator)
- [`generate()`](#generatelength-valueGenerator)
- [`padLeft()`](#padLeftmaxLength-value)
- [`padRight()`](#padRightmaxLength-value)
- [`align()`](#alignlength-filling-padSide--left-cutSide--right)
- [`join()`](#joinseparator)
- [`matchSome()`](#matchSometests)
- [`matchEvery()`](#matchEverytests)
- [`toString()`](#toStringitemMapper--null-separator--null-emptyMsg--null)
- [`toJSON()`](#toJSONmapper--null)
- [`frame()`](#framesize-rejectIncomplete--true)
- [`audit()`](#audittester-frameSize--2)
- [`auditChunks()`](#auditChunkstester-chunkSize--2-rejectSticking--true)
- [`audit()` vs. `auditChunks()`](#audit-vs-auditChunks)
- [`expandTo()`](#expandTotarget-cb-prevs-init--0-includeSticking--false-maxIters--32)
- [`expandToLength()`](#expandToLengthlength-cb-prevs-init--0)
Iteration:
- [`forEachChunk()`](#forEachChunkchunkSize-cb)
- [`forEachFrame()`](#forEachFrameblockSize-cb-fullBlocksOnly--false)
Min and Max:
- [`min()`](#minmapper)
- [`max()`](#maxmapper)
- [`findMin()`](#findMinmapper)
- [`findMax()`](#findMaxmapper)
- [`findMinIndex()`](#findMinIndexmapper)
- [`findMaxIndex()`](#findMaxIndexmapper)
Difference:
- [`minPairDiff()`](#minPairDiffcomparer--null)
- [`maxPairDiff()`](#maxPairDiffcomparer--null)
- [`minDiff()`](#minDiffcomparer--null)
- [`maxDiff()`](#maxDiffcomparer--null)
- [`minDiffAhead()`](#minDiffAheadcomparer--null)
- [`maxDiffAhead()`](#maxDiffAheadcomparer--null)
- [`maxPairDiff()` vs. `maxDiff()` vs. `maxDiffAhead()`](#maxPairDiff-vs-maxDiff-vs-maxDiffAhead)
## `observe()`
Enriches array by additional methods:
- `on(type, handler)` - adds a handler for given action
- `off(type, handler)` - removes a handler for given action
- `cleanListeners(type)` - removes all handlers for given action
- `cleanAllListeners()` - removes all handlers for all actions
- `unobserve()` - makes array is no longer observable
> `type` parameter is one of the following: `push` or `pop`
```
const arr = [1,2,3,4];
arr.x.observable();
arr.on('push', (newArray, itemsAdded, newLength) => {
console.log('added new item(s):', itemsAdded);
});
arr.push(5,6);
// added new item(s): [5,6]
```
```
const arr = [1,2,3,4];
arr.x.observable();
arr.on('pop', (newArray, itemRemoved) => {
console.log('removed item:', itemRemoved);
});
arr.on('pop', (newArray, itemRemoved) => {
console.log('item has been removed:', itemRemoved);
});
arr.pop();
// removed item: 4
// item has been removed: 4
```
## `group(labelFactory...)`
```
[1, 2, 3, 4].x.group(x => x % 2 === 0 ? 'even' : 'odd')
// {
// odd: [1, 3],
// even: [2, 4]
// }
[1, 2, 30, 45].x.group(
x => x % 2 === 0 ? 'even' : 'odd',
x => x > 9 ? 'multidigit' : 'digit'
)
// {
// odd: [1, 45],
// even: [2, 30],
// digit: [1, 2],
// multidigit: [30, 45]
// }
[1, 2, 30, 45].x.group(
x => x % 2 === 0 ? 'even' : 'odd',
x => x > 9 ? 'multidigit' : null
)
// {
// odd: [1, 45],
// even: [2, 30],
// multidigit: [30, 45]
// }
[
{ name: 'Mark', city: 'LA' },
{ name: 'John', city: 'NY' },
{ name: 'Adam', city: 'LA' }
].x.group(u => u.city)
// {
// LA: [{ name: 'Mark', city: 'LA' }, { name: 'Adam', city: 'LA' }],
// NY: [{ name: 'John', city: 'NY' }]
// }
```
## `index(keySelector, valueSelector = null, fallbackKey = '__unindexed')`
- `keySelector`: `string` or `(item: any, index: number) => string`
- `valueSelector`: `string` or `(item: any, index: number) => string`
- `fallbackKey`: `string`
```
[
{ id: 1, name: 'John', city: 'NY' },
{ id: 2, name: 'Mark', city: 'LA' },
{ id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }
].x.index('city');
/*
{
NY: [ { id: 1, name: 'John', city: 'NY' } ],
LA: [
{ id: 2, name: 'Mark', city: 'LA' },
{ id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }
]
}
*/
```
```
[
{ id: 1, name: 'John', city: 'NY' },
{ id: 2, name: 'Mark', city: 'LA' },
{ id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }
].x.index(
u => u.city.toLowerCase(),
'id'
);
/*
{
ny: [ 1 ],
la: [ 2, 3 ]
}
*/
```
```
[
{ id: 1, name: 'John', city: 'NY' },
{ id: 2, name: 'Mark', city: 'LA' },
{ id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }
].x.index('extraKey', 'name');
/*
{
foo: [ 'Adam' ],
__unindexed: [ 'John', 'Mark' ]
}
*/
```
## `pivot(indexKey = null, valueSelector = null, keyMapper = null)`
```
const posts = [
{ id: 120, title: 'Post A', author: 'John' },
{ id: 121, title: 'Post B', author: 'Mark' },
{ id: 122, title: 'Post C', author: 'John' }
];
posts.x.pivot('author', 'title')
// {
// John: ['Post A', 'Post C'],
// Mark: ['Post B']
// }
```
```
const posts = [
{ id: 120, title: 'Post A', author: 'John' },
{ id: 121, title: 'Post B', author: 'Mark' },
{ id: 122, title: 'Post C', author: 'John' }
];
posts.x.pivot(
'author',
p => `[#${p.id}] ${p.title}`,
a => a.toLowerCase()
)
// {
// john: ['[#120] Post A', '[#122] Post C'],
// mark: ['[#121] Post B']
// }
```
```
const posts = [
{ id: 120, title: 'Post A', author: 'John' },
{ id: 121, title: 'Post B', author: 'Mark' },
{ id: 122, title: 'Post C', author: 'John' }
];
posts.x.pivot('author')
// {
// John: [
// { id: 120, title: 'Post A' },
// { id: 122, title: 'Post C' }
// ],
// Mark: [
// { id: 121, title: 'Post B' }
// ]
// }
posts.x.pivot('author', p => p)
// {
// John: [
// { id: 120, title: 'Post A', author: 'John' },
// { id: 122, title: 'Post C', author: 'John' }
// ],
// Mark: [
// { id: 121, title: 'Post B', author: 'Mark' }
// ]
// }
```
```
const arr = [
[1, 'foo'], [1, 'bar'], [2, 'baz']
];
arr.x.pivot(0, 1)
// {
// 1: [ 'foo', 'bar' ],
// 2: [ 'baz' ]
// }
```
## `joinOneToOne(source, targetKey, sourceKey, targetNewKey, sourceValueSelector = null)`
```
const posts = [
{ id: 1, title: 'Post 1', authorId: 100 },
{ id: 2, title: 'Post 2', authorId: 102 }
];
const authors = [
{ id: 100, name: 'John' },
{ id: 101, name: 'Mark' },
{ id: 102, name: 'Adam' }
];
posts.x.joinOneToOne(
authors,
'authorId',
'id',
'author',
author => author.name
)
/*
[
{ id: 1, title: 'Post 1', authorId: 100, author: 'John' },
{ id: 2, title: 'Post 2', authorId: 102, author: 'Adam' }
]
*/
```
## `joinOneToMany(source, targetKey, sourceKey, targetNewKey, sourceValueSelector = null)`
```
const authors = [
{ id: 100, name: 'John' },
{ id: 102, name: 'Adam' },
{ id: 101, name: 'Mark' }
];
const posts = [
{ id: 1, title: 'Post 1 by John', authorId: 100 },
{ id: 2, title: 'Post 1 by Adam', authorId: 102 },
{ id: 3, title: 'Post 2 by Adam', authorId: 102 },
{ id: 4, title: 'Post 2 by John', authorId: 100 }
];
authors.x.joinOneToMany(
posts,
'id',
'authorId',
'posts',
post => post.title
)
/*
[
{ id: 100, name: 'John', posts: ['Post 1 by John', 'Post 2 by John'] },
{ id: 102, name: 'Adam', posts: ['Post 1 by Adam', 'Post 2 by Adam'] },
{ id: 101, name: 'Mark', posts: [] }
]
*/
```
## `joinManyToOne(source, targetKey, sourceKey, targetNewKey, sourceValueSelector = null)`
```
const posts = [
{ id: 1, title: 'Post 1', tagsIds: [10,20] },
{ id: 2, title: 'Post 2', tagsIds: [20,40] },
{ id: 3, title: 'Post 3', tagsIds: [] },
{ id: 4, title: 'Post 4' },
];
const tags = [
{ id: 10, name: 'Tag A' },
{ id: 20, name: 'Tag B' },
{ id: 30, name: 'Tag C' },
{ id: 40, name: 'Tag D' }
];
posts.x.joinManyToOne(
tags,
'tagsIds',
'id',
'tags',
tag => tag.name
)
/*
[
{ id: 1, title: 'Post 1', tagsIds: [10, 20], tags: ['Tag A', 'Tag B'] },
{ id: 2, title: 'Post 2', tagsIds: [20, 40], tags: ['Tag B', 'Tag D'] },
{ id: 3, title: 'Post 3', tagsIds: [], tags: [] },
{ id: 4, title: 'Post 4', tags: [] }
]
*/
```
## `takeUntil(itemOrMatcher, inclusive = true)`
```
[1, 2, 3, 4, 5, 6].x.takeUntil(4)
// [1, 2, 3, 4]
[1, 2, 3, 4, 5, 6].x.takeUntil(4, false)
// [1, 2, 3]
[1, 3, 2, 4].x.takeUntil(x => x % 2 === 0)
// [1, 3, 2]
[1, 3, 2, 4].x.takeUntil(x => x % 2 === 0, false)
// [1, 3]
[1, 2, 3, 4, 5, 6].x.takeUntil(10)
// [1, 2, 3, 4, 5, 6]
```
## `takeUntilReduce(accTester, reducer, reducerInit, inclusive = true)`
```
// collect numbers until their average equals 5
const res = [6, 5, 4, 3, 2, 1].x.takeUntilReduce(
(acc) => acc.x.avg() === 5,
(a, b) => [...a, b],
[]
);
// [6, 5, 4]
```
```
[1, 2, 3, 4, 5, 6].x.takeUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0
)
// [1, 2, 3, 4]
[1, 2, 3, 4, 5, 6].x.takeUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0,
false
)
// [1, 2, 3]
[10, 20, 30, 40].x.takeUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0
)
// [10]
[10, 20, 30, 40].x.takeUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0,
false
)
// []
```
## `skipUntil(itemOrMatcher, inclusive = true)`
```
[1, 2, 3, 4, 5, 6].x.skipUntil(4)
// [4, 5, 6]
[1, 2, 3, 4, 5, 6].x.skipUntil(4, false)
// [5, 6]
[1, 3, 2, 4].x.skipUntil(x => x % 2 === 0)
// [2, 4]
[1, 3, 2, 4].x.skipUntil(x => x % 2 === 0, false)
// [4]
[1, 2, 3, 4, 5, 6].x.skipUntil(10)
// []
```
## `skipUntilReduce(accTester, reducer, reducerInit, inclusive = true)`
```
[1, 2, 3, 4, 5, 6].x.skipUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0
)
// [4, 5, 6]
[1, 2, 3, 4, 5, 6].x.skipUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0,
false
)
// [5, 6]
[10, 20, 30, 40].x.skipUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0
)
// [10, 20, 30, 40]
[10, 20, 30, 40].x.skipUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0,
false
)
// [20, 30, 40]
[1, 2, 1, 2].x.skipUntilReduce(
sum => sum >= 10,
(a, b) => a + b,
0
)
// []
```
## `extractSub(pattern, multi = true, overlay = true)`
```
[1,2,3,4,1,2,3,4].x.extractSub(
[1,2]
)
// [
// [ 0, [1,2] ],
// [ 4, [1,2] ]
// ]
```
```
const even = x => x % 2 === 0;
const odd = x => x % 2 !== 0;
[1,2,3,4,1,2,3,4].x.extractSub(
[1,even,odd]
)
// [
// [ 0, [1,2,3] ],
// [ 4, [1,2,3] ]
// ]
```
* get only one:
```
[1,2,3,4,1,2,3,4].x.extractSub(
[1,even,odd], false
)
// [ 0, [1,2,3] ]
```
```
const arr = [1,2,4,6,3,5];
arr.x.extractSub(
[even, even]
)
// [ [1,[2,4]], [2,[4,6]] ]
arr.x.extractSub(
[even,even], true, false
)
// [ [1,[2,4]] ]
```
## `uniq([selector])`
```
[1, 2, 3, 2, 1].x.uniq()
// [1, 2, 3]
[{x:10,y:10}, {x:10,y:20}, {x:20,y:20}, {x:20,y:10}].x.uniq(coords => coords.x)
// [{x:10,y:10}, {x:20,y:20}]
```
## `uniqAdjacent([selector])`
```
[1, 2, 3, 2, 1].x.uniqAdjacent()
// [1, 2, 3, 2, 1]
[1, 2, 3, 3, 2, 2, 1, 1].x.uniqAdjacent()
// [1, 2, 3, 2, 1]
[
{ x: 1, id: 100 },
{ x: 2, id: 101 },
{ x: 3, id: 102 },
{ x: 3, id: 103 },
{ x: 2, id: 104 },
{ x: 2, id: 105 },
{ x: 1, id: 106 },
{ x: 1, id: 107 }
].x.uniqAdjacent(obj => obj.x)
// [
// { x: 1, id: 100 },
// { x: 2, id: 101 },
// { x: 3, id: 102 },
// { x: 2, id: 104 },
// { x: 1, id: 106 }
// ]
```
### `uniq()` vs. `uniqAdjacent()`
```
const arr = [1, 2, 2, 3, 3, 2, 1, 1];
arr.x.uniq()
// [1, 2, 3]
arr.x.uniqAdjacent()
// [1, 2, 3, 2, 1]
```
## `nth(index)`
```
[1,2,3,4].x.nth(0)
// 1
[1,2,3,4].x.nth(-1)
// 4
```
## `multiple(indexes)`
```
['foo', 'bar', 'baz', 'quux'].x.multiple([1,3])
// ['bar', 'quux']
```
## `last()`
```
[1,2,3,4].x.last()
// 4
[10].x.last()
// 10
[].x.last()
// undefined
```
## `pluck(path)`
```
[
{ foo: { bar: [1,2,{ baz: 120 }] } },
{ foo: { bar: [3,4,{ baz: 340 }] } },
{ foo: { bar: [5,6,{ baz: 560 }] } }
].x.pluck('foo.bar[2].baz')
// other working variant: pluck('foo.bar.2.baz')
// [120, 340, 560]
```
```
[
[1,2,3],
[2,3,5],
[3,4,7],
[4,5,9]
].x.pluck(2)
// other working variants: pluck('2'), pluck('[2]')
// [3, 5, 7, 9]
```
## `pluckMultiple(...paths)`
```
[
{ foo: { bar: [1,2,{ baz: 120 }] } },
{ foo: { bar: [3,4,{ baz: 340 }] } },
{ foo: { bar: [5,6,{ baz: 560 }] } }
].x.pluckMultiple('foo.bar[0]', 'foo.bar[1]', 'foo.bar[2].baz')
// [ [1,2,120], [3,4,340], [5,6,560] ]
```
```
[
[1,2,3],
[2,3,5],
[3,4,7],
[4,5,9]
].x.pluckMultiple(0, 2)
// [ [1,3], [2,5], [3,7], [4,9] ]
```
## `glue(otherArray = [], headSize, tailSize = headSize)`
```
[1,2,3,4].x.glue([5,6,7,8], 2)
// [1,2,7,8]
[1,2,3,4].x.glue([5,6,7,8], 1, 3)
// [1,6,7,8]
[1,2,3,4].x.glue([5,6,7,8], 3, 1)
// [1,2,3,8]
```
## `glueHeads(otherArray = [], leftSize, rightSize = leftSize)`
```
[1,2,3,4].x.glueHeads([5,6,7,8], 2)
// [1,2,5,6]
[1,2,3,4].x.glueHeads([5,6,7,8], 1, 3)
// [1,5,6,7]
[1,2,3,4].x.glueHeads([5,6,7,8], 3, 1)
// [1,2,3,5]
```
## `glueTails(otherArray = [], leftSize, rightSize = leftSize)`
```
[1,2,3,4].x.glueTails([5,6,7,8], 2)
// [3,4,7,8]
[1,2,3,4].x.glueTails([5,6,7,8], 1, 3)
// [4,6,7,8]
[1,2,3,4].x.glueTails([5,6,7,8], 3, 1)
// [2,3,4,8]
```
## `iterator()`
```
const iter = [1,2,3,4].x.iterator();
[...iter]
// [1,2,3,4]
[...iter]
// []
```
```
const iter = [1,2,3,4].x.iterator();
iter.next()
// {value: 1, done: false}
iter.next()
// {value: 2, done: false}
[...iter]
// [3,4]
iter.next()
// {value: undefined, done: true}
[...iter]
// []
```
## `revIterator()`
```
const revIter = [1,2,3,4].x.revIterator();
[...revIter]
// [4,3,2,1]
[...revIter]
// []
```
## `refIterator(refKey, nextKey, initItemIdx = 0)`
```
const blocks = [
{ id: 1, next: 2, data: 'A' },
{ id: 3, next: 4, data: 'C' },
{ id: 4, data: 'D' },
{ id: 2, next: 3, data: 'B' }
];
const chain = blocks.x.refIterator('id', 'next');
const path = [...chain].map(b => b.data).join(' -> ');
path
// 'A -> B -> C -> D'
```
```
const blocks = [
{ id: 1, next: 2, data: 'A' },
{ id: 3, next: 4, data: 'C' },
{ id: 4, data: 'D' },
{ id: 2, next: 3, data: 'B' }
];
const chain = blocks.x.refIterator('id', 'next', 1);
const path = [...chain].map(b => b.data).join(' -> ');
path
// 'C -> D'
```
```
const blocks = [
{ id: 1, next: 20, data: 'A' },
{ id: 2, next: 3, data: 'B' }
];
const chain = blocks.x.refIterator('id', 'next');
const path = [...chain].map(b => b.data).join(' -> ');
path
// 'A'
```
## `cyclicIterator()`
```
const ci = [1,2,3,4].x.cyclicIterator();
const res = [];
for (let i = 1; i <= 10; i++) {
res.push(ci.current);
ci.next();
}
console.log(
res.join('-')
);
// '1-2-3-4-1-2-3-4-1-2'
```
## `echoIterator(sticky = false)`
```
const echit = [1,2,3,4].x.echoIterator();
let chain = echit.current;
for (
let i = 1, _ = echit.next();
i++ < 12;
echit.next()
) chain += ' -> ' + echit.current;
chain
// '1 -> 2 -> 3 -> 4 -> 3 -> 2 -> 1 -> 2 -> 3 -> 4 -> 3 -> 2'
```
```
const echit = [1,2,3,4].x.echoIterator(true);
let chain = echit.current;
for (
let i = 1, _ = echit.next();
i++ < 12;
echit.next()
) chain += ' -> ' + echit.current;
chain
// '1 -> 2 -> 3 -> 4 -> 4 -> 3 -> 2 -> 1 -> 1 -> 2 -> 3 -> 4'
```
## `everyNth(n = 1, from = 0, to)`
```
[1,2,3,4,5,6].x.everyNth(2)
// [1,3,5]
[1,2,3,4,5,6].x.everyNth(2, 1)
// [2,4,6]
[1,2,3,4,5,6].x.everyNth(2, -3)
// [4,6]
[1,2,3,4,5,6].x.everyNth(2, -5)
// [2,4,6]
[1,2,3,4,5,6].x.everyNth(2, -5, -2)
// [2,4]
```
## `select(from = 0, to = -1, step = 1)`
```
[1,2,3,4,5,6].x.select(2,4)
// [3,4,5]
[1,2,3,4,5,6].x.select(2)
// [3,4,5,6]
[1,2,3,4,5,6].x.select(-2)
// [5,6]
[1,2,3,4,5,6].x.select(2,-2)
// [3,4,5]
[1,2,3,4,5,6].x.select(-3,-2)
// [4,5]
[1,2,3,4,5,6].x.select(0,-1,2)
// [1,3,5]
[1,2,3,4,5,6].x.select(1,-1,2)
// [2,4,6]
```
## `localize(itemOrMatcher)`
```
[10, 20, 30, 40].x.localize(20)
// [1, 20]
['x', 'xx', 'xxx', 'xx', 'x'].x.localize(str => str.length >= 3)
// [2, 'xxx']
[1, 2, 3, 4].x.localize(20)
// null
```
## `localizeAll(itemOrMatcher)`
```
[10, 20, 30, 40].x.localizeAll(20)
// [[1, 20]]
[10, 20, 10, 20].x.localizeAll(20)
// [[1, 20], [3, 20]]
['x', 'xx', 'xxx'].localizeAll(str => str.length >= 2)
// [[1, 'xx'], [2, 'xxx']]
[1, 2, 3, 4].x.localizeAll(20)
// []
```
## `localizeMin([mapper])`
```
[5, 6, 7, 8].x.localizeMin()
// [0, 5]
['xxxx', 'xx', 'xxx'].x.localizeMin(str => str.length)
// [1, 'xx']
[].x.localizeMin()
// null
```
## `localizeMax([mapper])`
```
[5, 6, 7, 8].x.localizeMax()
// [3, 8]
['xxxx', 'xx', 'xxx'].x.localizeMax(str => str.length)
// [0, 'xxxx']
[].x.localizeMax()
// null
```
## `min([mapper])`
```
[5, 6, 7, 8].x.min()
// 5
['xxxx', 'xx', 'xxx'].x.min(str => str.length)
// 2
[].x.min()
// null
```
## `findMin([mapper])`
```
[5, 6, 7, 8].x.findMin()
// 5
['xxxx', 'xx', 'xxx'].x.findMin(str => str.length)
// 'xx'
[].x.findMin()
// null
```
## `findMinIndex([mapper])`
```
[5, 6, 7, 8].x.findMinIndex()
// 0
['xxxx', 'xx', 'xxx'].x.findMinIndex(str => str.length)
// 1
[].x.findMinIndex()
// -1
```
## `max([mapper])`
```
[5, 6, 7, 8].x.max()
// 8
['xxxx', 'xx', 'xxx'].x.max(str => str.length)
// 4
[].x.max()
// null
```
## `findMax([mapper])`
```
[5, 6, 7, 8].x.findMax()
// 8
['xxxx', 'xx', 'xxx'].x.findMax(str => str.length)
// 'xxxx'
[].x.findMax()
// null
```
## `findMaxIndex([mapper])`
```
[5, 6, 7, 8].x.findMaxIndex()
// 3
['xxxx', 'xx', 'xxx'].x.findMaxIndex(str => str.length)
// 0
[].x.findMaxIndex()
// -1
```
## `count(itemOrMatcher)`
```
[10, 20, 10, 20].x.count(20)
// 2
[1, 2, 3, 4, 5, 6].x.count(x => x % 2 === 0)
// 3
['x', 'xx', 'xxx'].x.count(str => str.length >= 2)
// 2
[1, 2, 3, 4].count(20)
// 0
```
## `countAll([labelFactory...])`
```
['foo', 'bar', 'foo', 'baz'].x.countAll()
// { foo: 2, bar: 1, baz: 1 }
['xx', 'xxx', 'xx'].x.countAll(str => `ofLength${str.length}`)
// { ofLength2: 2, ofLength3: 1 }
[1, 2, 3, 4, 5, 6].x.countAll(
x => x % 2 === 0 ? 'even' : 'odd'
)
// { even: 3, odd: 3 }
[1, 23, 4, 56, 7, 89].x.countAll(
x => x % 2 === 0 ? 'even' : 'odd',
x => x > 9 ? 'multidigit' : 'digit'
)
// { even: 2, odd: 4, multidigit: 3, digit: 3 }
[1, 23, 4, 56, 7, 89].x.countAll(
x => x % 2 === 0 ? 'even' : 'odd',
x => x > 9 ? 'multidigit' : null
)
// { even: 2, odd: 4, multidigit: 3 }
[
{ name: 'Mark', city: 'LA' },
{ name: 'John', city: 'NY' },
{ name: 'Adam', city: 'LA' }
].x.countAll(u => u.city)
// { LA: 2, NY: 1 }
```
## `avg([mapper])`
```
[1, 2, 3, 4].x.avg()
// 2.5
['x', 'xx', 'xxx'].x.avg(str => str.length)
// 2
[
{ examId: 120, mark: 5 },
{ examId: 121, mark: 4 }
].x.avg(exam => exam.mark)
// 4.5
[].x.avg()
// null
```
## `wavg(weightsArrOrWeightSelector, [selector])`
```
[4].x.wavg([2])
// 4
// (4 * 2) / 2 -> 8 / 2
[3, 3].x.wavg([3, 2])
// 3
// (3 * 3 + 3 * 2) / (3 + 2) -> (9 + 6) / 5 -> 15 / 5
[
{ examId: 120, mark: 5 },
{ examId: 121, mark: 4 },
{ examId: 122, mark: 4.5 },
{ examId: 123, mark: 5.5 },
{ examId: 124, mark: 5 }
].x.wavg([1, 1, 3, 2, 4], exam => exam.mark)
// 4.863636363636363
// (5 + 4 + 4.5 * 3 + 5.5 * 2 + 5 * 4) / (1 + 1 + 3 + 2 + 4)
// (5 + 4 + 13.5 + 11 + 20) / 11 -> 53.5 / 11 -> 4.863636363636363
[
{ examId: 120, examWeight: 1, mark: 5 },
{ examId: 121, examWeight: 1, mark: 4 },
{ examId: 122, examWeight: 3, mark: 4.5 },
{ examId: 123, examWeight: 2, mark: 5.5 },
{ examId: 124, examWeight: 4, mark: 5 }
].x.wavg(
exam => exam.examWeight,
exam => exam.mark
)
// 4.863636363636363
// as above
```
## `sum([mapper])`
```
[1,2,3,4].x.sum()
// 10
['foo', 'bar', 'baz'].x.sum(str => str.length)
// 9
```
## `chunk(size, rejectStickingTail = false)`
```
[1,2,3,4,5,6].x.chunk(2)
// [ [1,2], [3,4], [5,6] ]
[1,2,3,4,5].x.chunk(2)
// [ [1,2], [3,4], [5] ]
[1,2,3,4,5].x.chunk(2, true)
// [ [1,2], [3,4] ]
[1,2,3,4].x.chunk(0)
// []
```
## `chunkByCallback(cb, matchedItemOpening = true)`
```
// open new chunk when meet even number
[1,2,3,4,5,6].x.chunkByCallback(x => x % 2 === 0)
// [ [1], [2,3], [4,5], [6] ]
// close current chunk when meet even number
[1,2,3,4,5,6].x.chunkByCallback(x => x % 2 === 0, false)
// [ [1,2], [3,4], [5,6] ]
// same as chunk(3)
[1,2,3,4,5,6,7,8].x.chunkByCallback(
(_, idx) => (idx + 1) % 3 === 0,
false
)
// [ [1,2,3], [4,5,6], [7,8] ]
```
## `chunkByReduce(reducer, init = null, tester = null)`
```
[1,2,3,4,5,6,7,8].x.chunkByReduce(
(acc, curr) => acc + curr,
null,
sum => sum >= 8
)
// [ [1,2,3,4],[5,6],[7,8] ]
```
```
[1,2,3,4,5,6,7,8].x.chunkByReduce(
(acc, curr) => [...acc, curr],
[],
arr => arr.length === 2
)
// [ [1,2],[3,4],[5,6],[7,8] ]
```
## `chunkByPattern(...sizes, rejectStickingTail = false)`
```
[1,2,3,4,5,6].x.chunkByPattern(2, 3)
// [ [1,2], [3,4,5], [6] ]
[1,2,3,4,5,6].x.chunkByPattern(2, 3, true)
// [ [1,2], [3,4,5] ]
```
## `chunkByGroup(grouper = item => 0)`
```
['foo', 'bar', 'baz', 'quux'].x.chunkByGroup(
str => str.length
)
// [ ['foo','bar','baz'], ['quux'] ]
```
* groups are sorted in ascending order, yet chunks themselves are not sorted:
```
[
'Alabama', 'Texas', 'Illinois',
'Arizona', 'Idaho'
].x.chunkByGroup(str => str[0])
// [ ['Alabama','Arizona'], ['Illinois','Idaho'], ['Texas'] ]
```
* if the callback returns a number, the smaller it is, the closer to the array's beginning the chunk will be:
```
[1,30,500,600,40,2].x.chunkByGroup(x => {
if (x < 10) return 1;
if (x < 100) return 2;
return 3;
});
// [ [1,2], [30,40], [500,600] ]
```
* grouping callback can be binary one, i.e., returning either `true` or `false`:
```
[1,2,3,4,5,6].x.chunkByGroup(x => x % 2 === 0)
// [ [1,3,5], [2,4,6] ]
```
In such a case, a chunk with items that matches the callback will be second, as results are sorted in ascending order and the callback returns one of two strings: `true` or `false`. To put items matching the callback at the beginning, either rewrite condition or put it into `()` preceded with `!`:
```
[1,2,3,4,5,6].x.chunkByGroup(x => !(x % 2 === 0))
// [ [2,4,6], [1,3,5] ]
```
## `partition(partitioner = item => 0)`
The _partitioner_ should return a number. If you want to handle strings, use [`chunkByGroup()`](#chunkByGroupgrouper--item--0).
The smaller _partitioner_ result is, the closer to the array's beginning the chunk will be:
```
[1,30,500,600,40,2].x.partition(x => {
if (x < 10) return 1;
if (x < 100) return 2;
return 3;
});
// [ [1,2], [30,40], [500,600] ]
```
```
[1,2,3,4,5,6].x.partition(x => x % 2 === 0)
// [ [1,3,5], [2,4,6] ]
// 1 % 2 === 0 -> false, false -> 0
// 2 % 2 === 0 -> true, true -> 1
[1,2,3,4,5,6].x.partition(x => !(x % 2 === 0))
// [ [2,4,6], [1,3,5] ]
// 1 % 2 === 0 -> false, !false -> true -> 1
// 2 % 2 === 0 -> true, !true -> false -> 0
```
## `chunkReduce(size, reducer = null, init = null, rejectStickingTail = false)`
```
[1,2,3,4,5,6].x.chunkReduce(
2, (acc, curr) => acc + curr
)
// [3,7,11]
[1,2,3,4,5,6].x.chunkReduce(
3, (acc, curr) => acc + curr
)
// [6,15]
```
```
[1,2,3,4,5].x.chunkReduce(
2, (acc, curr) => acc + curr
)
// [3,7,5]
[1,2,3,4,5].x.chunkReduce(
2, (acc, curr) => acc + curr,
null, true
)
// [3,7]
```
* double first member of every pair:
```
[1,2,3,4,5,6].x.chunkReduce(
2, (acc, curr) => acc + curr,
chunk => chunk[0]
)
// [4,10,16]
// [1,2] -> [1,1,2] => 1 + 1 + 2 = 4
// [3,4] -> [3,3,4] => 3 + 3 + 4 = 10
// [5,6] -> [5,5,6] => 5 + 5 + 6 = 16
```
## `frameReduce(size, reducer = null, init = null, rejectStickingTail = false)`
```
[1,2,3,4].x.frameReduce(
2, (acc, curr) => acc + curr
)
// [3,5,7,4]
// [1+2, 2+3, 3+4, 4]
[1,2,3,4].x.frameReduce(
2, (acc, curr) => acc + curr,
null, true
)
// [3,5,7]
// [1+2, 2+3, 3+4]
```
```
[1,2,3,4,5,6].x.frameReduce(
3, (acc, curr) => acc + curr,
null, true
)
// [6,9,12,15]
// [1+2+3, 2+3+4, 3+4+5, 4+5+6]
```
## `zip(otherArrays...)`
```
['a', 'b', 'c', 'd'].x.zip([1, 2, 3, 4])
// [ ['a', 1], ['b', 2], ['c', 3], ['d', 4] ]
['a', 'b'].x.zip([1, 2], ['foo', 'bar'])
// [ ['a', 1, 'foo'], ['b', 2, 'bar'] ]
['a', 'b', 'c', 'd'].x.zip([1, 2])
// [ ['a', 1], ['b', 2] ]
['a', 'b'].x.zip([1, 2, 3, 4])
// [ ['a', 1], ['b', 2] ]
```
## `zipAll(otherArrays...)`
```
['a', 'b'].x.zipAll([1, 2])
// [ ['a', 1], ['b', 2] ]
['a', 'b'].x.zipAll([1, 2], ['foo', 'bar'])
// [ ['a', 1, 'foo'], ['b', 2, 'bar'] ]
['a', 'b', 'c', 'd'].x.zipAll([1, 2])
// [ ['a', 1], ['b', 2], ['c', undefined], ['d', undefined] ]
['a', 'b'].x.zipAll([1, 2, 3, 4])
// [ ['a', 1], ['b', 2], [undefined, 3], [undefined, 4] ]
```
### `zip()` vs. `zipAll()`
```
[1, 2, 3, 4].x.zip(['a', 'b'])
// [ [1, 'a'], [2, 'b'] ]
[1, 2, 3, 4].x.zipAll(['a', 'b'])
// [ [1, 'a'], [2, 'b'], [3, undefined], [4, undefined] ]
```
## `unzip()`
```
[ ['a', 1], ['b', 2], ['c', 3], ['d', 4] ].x.unzip()
// [ ['a', 'b', 'c', 'd'], [1, 2, 3, 4] ]
[ ['a', 1, 'foo'], ['b', 2, 'bar'], ['c', 3, 'baz'] ].x.unzip()
// [ ['a', 'b', 'c'], [1, 2, 3], ['foo', 'bar', 'baz'] ]
[ ['a', 1], ['b', 2, 'foo'], ['c', 3, 'bar']].x.unzip()
// [ ['a', 'b'], [1, 2] ]
```
## `unzipAll()`
```
[ ['a', 1, 'foo'], ['b', 2, 'bar'], ['c', 3, 'baz'] ].x.unzipAll()
// [ ['a', 'b', 'c'], [1, 2, 3], ['foo', 'bar', 'baz'] ]
[ ['a', 1], ['b', 2, 'foo'], ['c', 3, 'bar']].x.unzipAll()
// [ ['a', 'b', 'c'], [1, 2, 3], [undefined, 'foo', 'bar'] ]
```
### `unzip()` vs. `unzipAll()`
```
[ ['a', 1], ['b', 2, 'foo'] ].x.unzip()
// [ ['a', 'b'], [1, 2] ]
[ ['a', 1], ['b', 2, 'foo'] ].x.unzipAll()
// [ ['a', 'b'], [1, 2], [undefined, 'foo'] ]
```
## `split(matcher = null)`
```
[1,2,3,2,4,5].x.split(2)
// [ [1], [3], [4,5] ]
[1,2,3,4,5,6].x.split([2,4,8])
// [ [1], [3], [5,6] ]
[0,1,2,4,3,5,6,8,7,9].x.split(x => x % 2 === 0)
// [ [1], [3,5], [7,9] ]
[1,2,3,4].x.split(8)
// [1,2,3,4]
```
## `splitByPattern(take, skip, from = null, rejectStickingTail = false)`
```
[1,2,3,4,5,6,7,8].x.splitByPattern(3, 2)
// [ [1,2,3], [6,7,8] ]
[1,2,3,4,5,6,7,8].x.splitByPattern(3, 2, 1)
// [ [2,3,4], [7,8] ]
[1,2,3,4,5,6,7,8].x.splitByPattern(3, 2, 1, true)
// [ [2,3,4] ]
```
## `cut([index])`
```
[1,2,3,4].x.cut()
// [ [1, 2], [3, 4] ]
[1,2,3,4,5].x.cut()
// [ [1,2], [3,4,5] ]
[1,2,3,4,5,6].x.cut(2)
// [ [1,2], [3,4,5,6] ]
[1,2,3,4,5,6].x.cut(-2)
// [ [1,2,3,4], [5,6] ]
```
## `chop(...cuts)`
```
[1,2,3,4,5,6].x.chop(2)
// [ [1,2], [3,4,5,6] ]
[1,2,3,4,5,6].x.chop(-2)
// [ [1,2,3,4], [5,6] ]
[1,2,3,4,5,6].x.chop(-2, 2)
// [ [1,2], [3,4], [5,6] ]
[1,2,3,4,5,6,7,8].x.chop(-2, 2)
// [ [1,2], [3,4,5,6], [7,8] ]
```
```
[1,2,3,4,5,6,7,8,9,0].chop(
1, 3, 6, 12
)
// [ [1], [2,3], [4,5,6], [7,8,9,0] ]
```
## `wrapAlter(processor)`
```
const arr = [1,2,3,4];
const alt = arr.x.wrapAlter(a => {
a.pop();
a.push(5);
});
alt
// [1,2,3,5]
arr
// [1,2,3,4]
```
```
const arr = [1,2,3,4];
const alt = arr.x.wrapAlter(a => {
a.pop();
a.push(5);
return a.slice(2);
});
alt
// [3,5]
arr
// [1,2,3,4]
```
## `adapt(otherArray = [], adaptations = 1)`
* adapt only one different value from other array:
```
[1,2,1,2].x.adapt([1,20,1,20])
// [1,20,1,2]
```
* adapt two different values from other array:
```
[1,2,1,2,1,2].x.adapt([1,20,1,20,1,20], 2)
// [1,20,1,20,1,2]
```
* adapt all different values from other array (use `-1` as a second parameter):
```
[1,2,1,2,1,2].x.adapt([1,20,1,20,1,20], -1)
// [1,20,1,20,1,20]
```
## `adaptByCallback(otherArray = [], tester = null, adaptations = 1, lazy = false)`
> default tester: `(a, b) => a !== b`
```
[1,2,3,4,5,6].x.adaptByCallback(
[1,2,30,40,50,60],
(a, b) => (a % 2 === 0) && (b % 2 === 0)
)
// [1,2,3,40,5,6]
// adapt first other value if both target and source value are even
```
* in _lazy mode_, every other value is counted whether or not has been adapted:
```
// normal mode
[1,2,3,4,5,6].x.adaptByCallback(
[1,2,30,40,50,60],
x => x % 2 === 0,
2
)
// [1,2,3,40,5,60]
// lazy mode
[1,2,3,4,5,6].x.adaptByCallback(
[1,2,30,40,50,60],
x => x % 2 === 0,
2,
true
)
// [1,2,3,40,5,6]
```
## `random(count = 1)`
```
[1,2,3,4,5,6].x.random()
// 4 (for example)
[1,2,3,4,5,6].x.random(1)
// 4 (for example)
[1,2,3,4,5,6].x.random(2)
// [5,2] (for example)
[1,2,3,4,5,6].x.random(4)
// [3,6,1,5] (for example)
```
## `shuffle()`
```
[1,2,3,4].x.shuffle()
// [4,2,3,1] (for example)
```
## `move(sourceIndex, count, targetIndex = 0)`
```
// move first 3 items to index 2
[1,2,3,4,5,6].x.move(0, 3, 2)
// [4,5,1,2,3,6]
// move last 2 items to index 0
[1,2,3,4,5,6].x.move(-2, 2)
// [5,6,1,2,3,4]
// move first item to the end
[1,2,3,4,5,6].x.move(0, 1, -1)
// [2,3,4,5,6,1]
// move last item to the beginning
[1,2,3,4,5,6].x.move(-1, 1)
// [6,1,2,3,4,5]
// move second to last item to the end (swap two last items)
[1,2,3,4,5,6].x.move(-2, 1, -1)
// [1,2,3,4,6,5]
```
## `insert(index, ...items)`
```
[1,2,3,4].x.insert(0, 10, 20)
// [10,20,1,2,3,4]
[1,2,3,4].x.insert(2, 10, 20)
// [1,2,10,20,3,4]
[1,2,3,4].x.insert(-1, 10, 20)
// [1,2,3,10,20,4]
```
## `overwrite(index, ...items)`
```
[1,2,3,4,5,6].x.overwrite(0, 7, 8)
// [7,8,3,4,5,6]
[1,2,3,4,5,6].x.overwrite(2, 7, 8)
// [1,2,7,8,5,6]
[1,2,3,4,5,6].x.overwrite(-1, 7, 8)
// [1,2,3,4,5,7,8]
```
## `override(index, ...items)`
```
[1,2,3,4,5,6].x.override(0, 7, 8)
// [7,8,3,4,5,6]
[1,2,3,4,5,6].x.override(2, 7, 8)
// [1,2,7,8,5,6]
[1,2,3,4,5,6].x.override(-1, 7, 8)
// [1,2,3,4,5,7]
```
### `overwrite()` vs. `override()`
```
[1,2,3,4].x.overwrite(2, 5, 6, 7, 8, 9)
// [1,2,5,6,7,8,9]
[1,2,3,4].x.override(2, 5, 6, 7, 8, 9)
// [1,2,5,6]
```
## `cork(value)`
```
[1,2,3,4].x.cork(0)
// [1,0,2,0,3,0,4]
```
```
[1,2,3,4].x.cork([10,20])
// [1,10,2,20,3,10,4]
```
```
[1,2,3,4].x.cork(
(curr, next, idx) => (curr + next) / 2
)
// [1, 1.5, 2, 2.5, 3, 3.5, 4]
```
## `precede(appendix = [], fillWithUndefined = false)`
```
[1,2,3,4].x.precede(0)
// [0,1,0,2,0,3,0,4]
[1,2,3,4].x.precede([10,20,30,40])
// [10,1,20,2,30,3,40,4]
[1,2,3,4].x.precede(x => x * 2)
// [2,1,4,2,6,3,8,4]
```
```
[1,2].x.precede([10,20,30,40])
// [10,1,20,2]
[1,2,3,4].x.precede([10,20])
// [10,1,20,2,3,4]
[1,2,3,4].x.precede([10,20], true)
// [10,1,20,2,undefined,3,undefined,4]
```
## `follow(appendix = [], fillWithUndefined = false)`
```
[1,2,3,4].x.follow(0)
// [1,0,2,0,3,0,4,0]
[1,2,3,4].x.follow([10,20,30,40])
// [1,10,2,20,3,30,4,40]
[1,2,3,4].x.follow(x => x * 2)
// [1,2,2,4,3,6,4,8]
```
```
[1,2].x.follow([10,20,30,40])
// [1,10,2,20]
[1,2,3,4].x.follow([10,20])
// [1,10,2,20,3,4]
[1,2,3,4].x.follow([10,20], true)
// [1,10,2,20,3,undefined,4,undefined]
```
## `frozen()`
Disable addition, removal and update.
> alias: `readonly()` or `readOnly()`
```
const arr = [10,20];
arr.x.frozen();
arr.push(30)
// TypeError: Cannot add property 2, object is not extensible
arr.pop()
// TypeError: Cannot delete property '1' of [object Array]
arr[0] = 100
arr
// [10,20]
```
## `fixed()`
Disable addition and removal.
```
const arr = [10,20];
arr.x.fixed();
arr.push(30)
// TypeError: Cannot add property 2, object is not extensible
arr.pop()
// TypeError: Cannot delete property '1' of [object Array]
arr[0] = 100
arr
// [100,20]
```
## `dwarf()`
Disable addition.
```
const arr = [10,20];
arr.x.dwarf();
arr.push(30)
// TypeError: Cannot add property 2, object is not extensible
arr.pop()
arr[0] = 100
arr
// [100]
```
## `alterable()`
Return array with enabled addition, removal and update.
```
const frozen = [1,2,3,4].x.frozen();
const fixed = [1,2,3,4].x.fixed();
const dwarf = [1,2,3,4].x.dwarf();
const unfrozen = frozen.x.alterable();
const unfixed = fixed.x.alterable();
const undwarf = dwarf.x.alterable();
unfrozen.push(5);
unfixed.push(5);
undwarf.push(5);
unfrozen // [1,2,3,4,5]
unfixed // [1,2,3,4,5]
undwarf // [1,2,3,4,5]
```
## `clamped(min, max)`
```
const rgb = [].x.clamped(0, 255);
rgb.push(-120);
rgb.push(250);
rgb.push(340);
rgb
// [0, 250, 255]
```
```
const digits = [-20,-4,1,8,12,28].x.clamped(-9, 9);
// [-9,-4,1,8,9,9]
digits.push(-120);
digits.push(4);
digits.push(120);
digits
// [-9,-4,1,8,9,9,-9,4,9]
```
## `fold(coverSelector)`/`unfold(index)`
```
const users = [
{ name: 'John', city: 'LA' },
{ name: 'Mark', city: 'NY' }
].x.fold(u => u.name);
console.log(users[0]);
// John
console.log(users[1]);
// Mark
console.log(users.x.unfold(0));
// { name: 'John', city: 'LA' }
console.log(users.x.unfold(1));
// { name: 'Mark', city: 'NY' }
```
```
const users = [
{ name: 'John', city: 'LA' }
].x.fold(u => u.name);
users.push({ name: 'Mark', city: 'NY' });
console.log([...users]);
// John, Mark
console.log(
users.x.unfold(0),
users.x.unfold(1)
);
// { name: 'John', city: 'LA' }
// { name: 'Mark', city: 'NY' }
```
## `sparse(slots = 1)`
```
[1, 2, 3, 4].x.sparse()
// [1, empty, 2, empty, 3, empty, 4]
[1, 2, 3, 4].x.sparse(1)
// [1, empty, 2, empty, 3, empty, 4]
[1, 2, 3, 4].x.sparse(2)
// [1, empty, empty, 2, empty, empty, 3, empty, empty, 4]
```
## `thick()`
```
[1,2,,,,3,,,4].x.thick()
// [1,2,3,4]
```
## `union(otherSet = [])`
```
[1,2,3,4].x.union([5,6,7,8])
// [1,2,3,4,5,6,7,8]
[1,2,3,4].x.union([3,4,5,6])
// [1,2,3,4,5,6]
```
## `intersection(otherSet = [])`
```
[1,2,3,4].x.intersection([3,4,5,6])
// [3,4]
```
## `diff(otherSet = [])`
```
[1,2,3,4].x.diff([3,4,5,6])
// [1,2]
```
## `symDiff(otherSet = [])`
```
[1,2,3,4].x.symDiff([3,4,5,6])
// [1,2,5,6]
```
## `complement(otherSet = [])`
```
[1,2,3,4].x.complement([3,4,5,6])
// [5,6]
```
## `reduce(reducer, finalizer = null, init)`
```
[1,2,3,4].x.reduce(
(acc, curr) => acc + curr,
(sum, arr) => sum / arr.length
)
// 2.5 (average)
```
```
[1,4,2,3].x.reduce(
([currMax, itsIdx], curr, idx) => {
return idx === 0 || curr > currMax ? [curr,idx] : [currMax,itsIdx];
},
([currMax, itsIdx]) => itsIdx,
[]
)
// 1 (index of largest number)
```
## `scan(reducer, init = null)`
$scanArr_i = reduce(inputArr_{<0;i>}\color{#888}{, init}\color{#000})$
```
[1,2,3,4].x.scan((a, c) => a + c)
// [1,3,6,10]
// 1 -> 1
// 3 -> 1 + 2
// 6 -> 1 + 2 + 3 or 3 + 3
// 10 -> 1 + 2 + 3 + 4 or 6 + 4
```
```
['f', 'o', 'o'].x.scan((a, c) => a + c)
// ['f', 'fo', 'foo']
['b', 'a', 'r'].x.scan((a, c) => a + c, 'foo')
// ['foob', 'fooba', 'foobar']
```
## `sample(size, step = 1)`
```
[1,2,3,4,5,6].x.sample(2)
// [ [1,2],[2,3],[3,4],[4,5],[5,6] ]
[1,2,3,4,5,6].x.sample(2, 2)
// [ [1,2],[3,4],[5,6] ]
[1,2,3,4,5,6].x.sample(2, 3)
// [ [1,2],[4,5] ]
```
## `flat(level = -1)`
* by default, `flat()` without parameter (or with default parameter `-1`) performs deep flat:
```
[1,2,[3,4,[5,6],7,8],9,10].x.flat()
// [1,2,3,4,5,6,7,8,9,10]
```
* with parameter given, depth of flattening can be limited:
```
[1,[2,[3,[4,[5,[6,7],8],9],10],11],12].x.flat(3)
// [1,2,3,4,[5,[6,7],8],9,10,11,12]
```
## `sort(...comparators)`
every `comparator` can be:
- one-sign string:
* `-` to sort primitive values in descending order
* `+` to sort primitive values in ascending order
- string that contains key to sort by
* `foo` to sort by `foo` key in ascending order
* `+foo` to sort by `foo` key in ascending order
* `-foo` to sort by `foo` key in descending order
- callback - compare function like one passed to native `sort()` method
```
[2,3,1,4].x.sort()
// [1,2,3,4]
```
```
const arr = [
{ name: 'Mark', age: 20 },
{ name: 'John', age: 20 },
{ name: 'Adam', age: 22 }
];
arr.x.sort(
'-age', 'name'
);
arr.map(u => u.name)
// ['Adam', 'John', 'Mark']
```
```
['C', 'A', 'D', 'B'].x.sort('-')
// ['D', 'C', 'B', 'A']
```
## `reversed()`
Return reversed array keeping original array unchanged.
```
const arr = [1,2,3,4];
const rev = arr.x.reversed();
rev
// [4,3,2,1]
arr
// [1,2,3,4]
// original array unchanged
```
## `forEach(cb)`
Like `forEach()` but with `break`/`continue` feature.
> alias: `each()`
```
[1,2,3,4].x.forEach((item, idx, api, array) => {
// api - break and continue
if (x === 4) return api.break;
console.log(item);
});
// 1 2 3
```
```
[1,2,3,4].x.forEach((item, idx, api) => {
if (x % 2 !== 0) return api.continue;
console.log(item);
});
// 2 4
```
```
[1,2,3,4,5,6].x.forEach((
item,
idx,
flow
) => {
if (item % 2 !== 0) return flow.continue;
console.log(item);
if (item === 4) return flow.break;
});
// 2 4
```
## `spreadMap(mapper)`
```
[1,2,3,4].x.spreadMap(x => [x, x * x])
// [1,1,2,4,3,9,4,16]
[1,2,3,4].x.spreadMap(x => x * x)
// [1,4,9,16]
```
```
[1,2,3,4].x.spreadMap(x => [
x,
x % 2 === 0
])
// [1, false, 2, true, 3, false, 4, true]
```
## `filterMapped(mapper = null, filter = null)`
Filters items that would pass a test if they were mapped.
```
[1,2,3,4,5,6,7,8,9].x.filterMapped(
x => Math.sqrt(x),
x => x === parseInt(x)
)
// [1,4,9]
// numbers whose square root is integer
```
```
['cat', 'dog', 'lion', 'tiger'].x.filterMapped(
str => str.length,
len => len > 3
)
// ['lion', 'tiger']
```
## `replace(matcher = x => x, replacer = x => x)`
```
[1,2,1,2].x.replace(
2,
10
)
// [1,10,1,10]
[1,2,3,4].x.replace(
[1,2,5,6],
10
)
// [10,10,3,4]
[1,2,3,4].x.replace(
x => x % 2 === 0,
10
)
// [1,10,3,10]
```
```
let item = 2;
[1,2,1,2].x.replace(
item,
x => x * 10
)
// [1,20,1,20]
[1,2,3,4].x.replace(
[3,4,5,6],
x => x * 10
)
// [1,2,30,40]
[1,2,3,4].x.replace(
x => x % 2 === 0,
x => x * 10
)
// [1,20,3,40]
```
## `findIndexes(matcher = null)`
```
[1,2,3,4,5,6].x.findIndexes(x => x % 2 === 0)
// [1,3,5]
[1,2,3,4,5,6].x.findIndexes([2,4])
// [1,3]
[1,2,3,1,2,3].x.findIndexes(3)
// [2,5]
[1,2,3,1,2,3].x.findIndexes()
// []
```
## `includes(matcher = null)`
```
[1,2,3,4].x.includes(4)
// true
[1,2,3,4].x.includes([4,8])
// true
[1,2,3,4].x.includes(x => x % 2 === 0)
// true
[1,2,3,4].x.includes(x => x >= 100)
// false
[1,2,3,4].x.includes()
// false
```
## `mapReduce(chunkSize = 2, reducer = null, init = null, rejectStickingTail = false)`
> default reducer: `(a, c) => a + c`
```
[10,20,30,40].x.mapReduce(
2, (acc, curr) => acc + curr
)
// [30,70]
```
```
['<', 'div', '>', '<', 'span', '>'].x.mapReduce(
3, (acc, curr) => acc + curr
)
// ['
', '']
```
* `init` parameter can be a literal value or a callback:
```
[1,2,3,4,5,6].x.mapReduce(
2,
(acc, curr) => acc + curr,
(chunk, chunkNumber) => chunkNumber
)
// [4,9,14]
// [1+1+2, 2+3+4, 3+5+6]
// [1+3, 2+7, 3+11]
```
```
// average of every triplets
// sticking tail rejected
[1,2,3,4,5,6,7,8].x.mapReduce(
3,
(acc, curr, idx, chunk) => {
let res = acc + curr;
if (idx === chunk.length - 1) res = res / chunk.length;
return res;
},
null,
true
)
// [2,5]
// [(1+2+3)/3, (4+5+6)/3]
```
## `pop(n = 1)`
```
const arr = [1,2,3,4,5,6];
arr.x.pop(2);
// returns removed items: [5,6]
arr
// [1,2,3,4]
```
## `shift(n = 1)`
```
const arr = [1,2,3,4,5,6];
arr.x.shift(2);
// returns removed items: [1,2]
arr
// [3,4,5,6]
```
## `pull(index)`
```
const arr = [1,2,3,4];
arr.x.pull(2)
// 3
arr
// [1,2,4]
```
```
const arr = [1,2,3,4];
arr.x.pull()
// 4
arr
// [1,2,3]
```
## `remove(valuesOrCb)`
* single literal value:
```
const arr = [1,2,3,2,1];
arr.x.remove(2)
// [2,2]
arr
// [1,3,1]
```
* array of values:
```
const arr = [1,2,3,4,3,2,1];
arr.x.remove([1,2,8])
// [1,2,2,1]
arr
// [3,4,3]
```
* callback:
```
const arr = [1,2,3,4];
arr.x.remove(x => x % 2 !== 0)
// [1,3]
arr
// [2,4]
```
## `fill(valueOrGenerator)`
```
[1,2,3,4].x.fill(0)
// [0,0,0,0]
```
```
[0,0,0,0].x.fill([1,2])
// [1,2,1,2]
```
```
(new Array(4)).x.fill(idx => idx + 1)
// [1,2,3,4]
```
## `generate(length, valueGenerator)`
```
[].x.generate(4, 0)
// [0,0,0,0]
[].x.generate(4, idx => idx + 1)
// [1,2,3,4]
[1].x.generate(4, (idx, curr) => {
const lastItem = curr[curr.length - 1];
return lastItem * 10;
})
// [1,10,100,1000]
```
## `padLeft(maxLength, value)`
> alias: `pad()`
```
[1,2].x.padLeft(4, 0)
// [0,0,1,2]
[1,2].x.pad(4, 0)
// [0,0,1,2]
```
## `padRight(maxLength, value)`
```
[1,2].x.padRight(4, 0)
// [1,2,0,0]
[1,2,3,4,5,6].x.padRight(4, 0)
// [1,2,3,4,5,6]
```
## `align(length, filling, padSide = 'left', cutSide = 'right')`
```
[1,2,3,4].x.align(6, 0)
// [0,0,1,2,3,4]
[1,2,3,4].x.align(6, 0, 'right')
// [1,2,3,4,0,0]
[1,2,3,4].x.align(2)
// [1,2]
[1,2,3,4].x.align(2, null, null, 'left')
// [3,4]
```
## `join(separator)`
```
[1,2,3,4].x.join('-')
// '1-2-3-4'
```
```
[4,8,1,0,0,8,0,8,0].x.join(
(item, idx, accStr) => {
if (idx === 1) return ' ';
if (idx === 4 || idx === 6) return '-';
return '';
}
)
// '48 100-80-80'
```
## `matchSome(...tests)`
```
[1,2,3,4,5,6].x.matchSome(
x => x % 2 === 0,
x => x >= 3
)
// [2,3,4,5,6]
```
## `matchEvery(...tests)`
```
[1,2,3,4,5,6].x.matchEvery(
x => x % 2 === 0,
x => x >= 3
)
// [4,6]
```
## `toString(itemMapper = null, separator = null, emptyMsg = null)`
* define only mapper:
```
const arr = [1,2,3,4];
arr.x.toString(x => x * 10);
arr + ''
// '10,20,30,40'
```
* define only separator:
```
const arr = [1,2,3,4];
arr.x.toString(null, ' - ');
arr + ''
// '1 - 2 - 3 - 4'
```
* define only alternative text for empty array:
```
const arr = [];
arr.x.toString(null, null, 'no items');
arr + ''
// 'no items'
arr.push(1,2);
arr + ''
// '1,2'
```
* define mapper, separator and alternative text:
```
const arr = [
{ id: 1, name: 'John', city: 'LA' },
{ id: 2, name: 'Mark', city: 'NY' }
];
arr.x.toString(
u => u.name + ', ' + u.city,
'; ',
'no users'
);
arr + ''
// 'John, LA; Mark, NY'
arr.splice(0, 2);
arr + ''
// 'no users'
```
* remove custom stuff:
```
const arr = [1,2,3,4];
arr.x.toString(x => x * 10);
arr + ''
// '10,20,30,40'
arr.x.toString()
arr + ''
// '1,2,3,4'
```
## `toJSON(mapper = null)`
```
const arr = [1,2,3,4];
arr.x.toJSON(nums => {
const even = nums.filter(n => n % 2 === 0);
return even.map(n => n * 10);
});
JSON.stringify({
nums: arr
});
// '{"nums":[20,40]}'
```
```
const arr = [1,2,3,4];
arr.x.toJSON(nums => nums.reduce(
(acc, curr) => acc + curr
));
JSON.stringify({
numsSum: arr
});
// '{"numsSum":10}'
```
```
const arr = [1,2,3,4];
arr.x.toJSON(nums => nums.x.toString(
x => x * 10,
'/'
));
JSON.stringify({
str: arr
});
// '{"str":"10/20/30/40"}'
```
## `frame(size, rejectIncomplete = true)`
```
[1,2,3,4,5,6].x.frame(3)
// [ [1,2,3],[2,3,4],[3,4,5],[4,5,6] ]
[1,2,3,4,5,6].x.frame(3, false)
// [ [1,2,3],[2,3,4],[3,4,5],[4,5,6], [5,6], [6] ]
```
## `audit(tester, frameSize = 2)`
* check if items are sorted in ascending order:
```
[1,2,3,4].x.audit(
(l, r) => l <= r
)
// true
[1,4,2,3].x.audit(
(l, r) => l <= r
)
// false
```
* check if item is sum of preceding two items:
```
[1,2,3,5,8].x.audit(
(l, r, sum) => l + r === sum,
3
)
// true
```
* check if item is a string that starts with preceding item:
```
['foo', 'foo bar', 'foo bar baz'].x.audit(
(leftString, rightString) => rightString.indexOf(leftString) === 0
)
// true
```
## `auditChunks(tester, chunkSize = 2, rejectSticking = true)`
* check if every second word is reversed variant of preceding one:
```
['foo', 'oof', 'bar', 'rab'].x.auditChunks(
(word, reversed) => word === [...reversed].reverse().join('')
)
// true
```
* check if every three items fit shape `[A, B, A + B]`:
```
[1,2,3,4,5,9,14,18].x.auditChunk(
(l, r, sum) => l + r === sum,
3
)
// true
// 1 + 2 = 3 -> true
// 4 + 5 = 9 -> true
// -> true
// 14 and 18 are ignored as last chunk is "sticking"
// i.e., chunk length (2) is less than required chunk size (3)
// to include sticking chunk, use false as third parameter:
[1,2,3,4,5,9,14,18].x.auditChunk(
(l, r, sum) => l + r === sum,
3,
false
)
// false
// 1 + 2 = 3 -> true
// 4 + 5 = 9 -> true
// 14 + 18 = undefined -> false
// -> false
```
## `audit()` vs. `auditChunks()`
| Call | Comparisons performed |
|--|--|
| `[1,2,3,4,5,6].x.audit(...)` | `1,2`, `2,3`, `3,4`, `4,5` and `5,6` |
| `[1,2,3,4,5,6].x.auditChunks(...)` | `1,2`, `3,4` and `5,6` |
* `audit()` takes all possible chunks:
```
[10,20,14,18].x.audit(
(l, r) => l <= r
)
// false
// 10 <= 20 -> true
// 20 <= 14 -> false
// -> false
```
* `auditChunks()` takes only adjacent chunks:
```
[10,20,14,18].x.auditChunks(
(l, r) => l <= r
)
// true
// 10 <= 20 -> true
// 14 <= 18 -> true
// -> true
```
## `expandTo(target, cb, prevs, init = 0, includeSticking = false, maxIters = 32)`
> To compute another value, `N` last items are taken.
> `N` equals value of the `prevs` parameter if exists, otherwise it looks for arity of the `cb` function.
```
[1,1].x.expandTo(8, (a, b) => a + b)
// [1,1,2,3,5,8]
```
```
[1].x.expandTo(8, (a, b) => a + b, 2, 1)
// [1,2,3,5,8]
```
If `target` is a function, it should return a boolean indicating whether or not the expanding is going on:
```
[1].x.expandTo(
x => x <= 8,
(a, b) => a + b,
2, 1
)
// [1,2,3,5,8]
[1,2].x.expandTo(
x => x !== -4,
(a, b) => a - b,
2, 0, true
)
// [1,2,-1,3,-4]
```
## `expandToLength(length, cb, prevs, init = 0)`
> To compute another value, `N` last items are taken.
> `N` equals value of the `prevs` parameter if exists, otherwise it looks for arity of the `cb` function.
```
[1,1].x.expandToLength(8, (a, b) => a + b)
// [1,1,2,3,5,8,13,21]
[1].x.expandToLength(8, (a, b) => a + b, 2, 0)
// [1,1,2,3,5,8,13,21]
[1].x.expandToLength(8, (a, b) => a + b, 2, 1)
// [1,2,3,5,8,13,21,34]
```
```
['a', 'b', 'c'].x.expandToLength(6, (a, b) => a + b)
// ['a', 'b', 'c', 'bc', 'cbc', 'bccbc']
['a', 'b', 'c'].x.expandToLength(6, (a, b, c) => a + b + c)
// ['a', 'b', 'c', 'abc', 'bcabc', 'cabcbcabc']
```
```
['a', 'b', 'c'].x.expandToLength(6, (a, b, c) => a + b + c, 3)
// ['a', 'b', 'c', 'abc', 'bcabc', 'cabcbcabc']
// a + b + c -> abc => [a, b, c, abc]
// b + c + abc -> bcabc => [a, b, c, abc, bcabc]
// c + abc + bcabc -> cabcbcabc => [a, b, c, abc, bcabc, cabcbcabc]
['a', 'b', 'c'].x.expandToLength(6, (a, b) => a + b, 3)
// ['a', 'b', 'c', 'ab', 'bc', 'cab']
// a + b [c ignored] -> ab => [a, b, c, ab]
// b + c [ab ignored] -> bc => [a, b, c, ab, bc]
// c + ab [bc ignored] -> cab => [a, b, c, ab, bc, cab]
```
## `forEachChunk(chunkSize, cb)`
```
[1,2,3,4].x.forEachChunk(2, (chunk, chunkNumber, arr) => {
console.log(chunkNumber, chunk);
});
// 1, [1,2]
// 2, [3,4]
```
```
[1,2,3,4].x.forEachChunk(3, (chunk, chunkNumber, arr) => {
console.log(chunkNumber, chunk);
});
// 1, [1,2,3]
// 2, [4]
```
## `forEachFrame(blockSize, cb, fullBlocksOnly = false)`
```
[1,2,3,4,5].x.forEachFrame(3, (block, idx) => {
console.log(block);
});
// [1,2,3]
// [2,3,4]
// [3,4,5]
// [4,5]
// [5]
```
```
[1,2,3,4,5].x.forEachFrame(3, (block, idx) => {
console.log(block);
}, true);
// [1,2,3]
// [2,3,4]
// [3,4,5]
```
## `minPairDiff(comparer = null)`
> default comparer: `(l, r) => Math.abs(l - r)`
```
[20,10,40].x.minPairDiff()
// 10
// [ Math.abs(20 - 10), Math.abs(10 - 40) ]
// [ 10, 30 ]
// min -> 10
```
* with custom comparer:
```
[20,10,40].x.minPairDiff(
(l, r) => l - r
)
// -30
// [ 20 - 10, 10 - 40 ]
// [ 10, -30 ]
// min -> -30
```
## `maxPairDiff(comparer = null)`
> default comparer: `(l, r) => Math.abs(l - r)`
```
[20,10,40].x.maxPairDiff()
// 30
// [ Math.abs(20 - 10), Math.abs(10 - 40) ]
// [ 10, 30 ]
// max -> 30
```
* with custom comparer:
```
[20,10,40].x.maxPairDiff(
(l, r) => l - r
)
// 10
// [ 20 - 10, 10 - 40 ]
// [ 10, -30 ]
// max -> 10
```
## `minDiff(comparer = null)`
> default comparer: `(l, r) => Math.abs(l - r)`
```
[20,80,40,10].x.minDiff()
// 10
// [
// |20-80|, |20-40|, |20-10|,
// |80-20|, |80-40|, |80-10|,
// |40-20|, |40-80|, |40-10|,
// |10-20|, |10-80|, |10-40|
// ]
// [
// 60, 20, 10,
// 60, 40, 70,
// 20, 40, 30,
// 10, 70, 30
// ]
// min -> 10
```
* with custom comparer:
```
[20,80,40,10].x.minDiff(
(l, r) => l / r
)
// 0.125
// [
// 20/80, 20/40, 20/10,
// 80/20, 80/40, 80/10,
// 40/20, 40/80, 40/10,
// 10/20, 10/80, 10/40
// ]
// [
// 0.25, 0.5, 2,
// 4, 2, 8,
// 2, 0.5, 4,
// 0.5, 0.125, 0.25
// ]
// min -> 0.125
```
## `maxDiff(comparer = null)`
> default comparer: `(l, r) => Math.abs(l - r)`
```
[20,80,40,10].x.maxDiff()
// 70
// [
// |20-80|, |20-40|, |20-10|,
// |80-20|, |80-40|, |80-10|,
// |40-20|, |40-80|, |40-10|,
// |10-20|, |10-80|, |10-40|
// ]
// [
// 60, 20, 10,
// 60, 40, 70,
// 20, 40, 30,
// 10, 70, 30
// ]
// max -> 70
```
* with custom comparer:
```
[20,80,40,10].x.maxDiff(
(l, r) => l / r
)
// 8
// [
// 20/80, 20/40, 20/10,
// 80/20, 80/40, 80/10,
// 40/20, 40/80, 40/10,
// 10/20, 10/80, 10/40
// ]
// [
// 0.25, 0.5, 2,
// 4, 2, 8,
// 2, 0.5, 4,
// 0.5, 0.125, 0.25
// ]
// max -> 8
```
## `minDiffAhead(comparer = null)`
> default comparer: `(l, r) => Math.abs(l - r)`
```
[10 80 200 20].x.minDiffAhead()
// 10
// [
// |10-80|, |10-200|, |10-20|,
// |80-200|, |80-20|,
// |200-20|
// ]
//
// [
// 70, 190, 10,
// 120, 60,
// 180
// ]
//
// min -> 10
```
* with custom comparer:
```
[10 80 200 20].x.minDiffAhead(
(l, r) => l - r
)
// -190
// [
// 10-80, 10-200, 10-20,
// 80-200, 80-20,
// 200-20
// ]
//
// [
// -70, -190, -10,
// -120, 60,
// 180
// ]
//
// min -> -190
```
## `maxDiffAhead(comparer = null)`
> default comparer: `(l, r) => Math.abs(l - r)`
```
[10 80 200 20].x.maxDiffAhead()
// 190
// [
// |10-80|, |10-200|, |10-20|,
// |80-200|, |80-20|,
// |200-20|
// ]
//
// [
// 70, 190, 10,
// 120, 60,
// 180
// ]
//
// max -> 190
```
* with custom comparer:
```
[10 80 200 20].x.maxDiffAhead(
(l, r) => l - r
)
// 180
// [
// 10-80, 10-200, 10-20,
// 80-200, 80-20,
// 200-20
// ]
//
// [
// -70, -190, -10,
// -120, 60,
// 180
// ]
//
// max -> 180
```
## `maxPairDiff()` vs. `maxDiff()` vs. `maxDiffAhead()`
* `maxPairDiff()` compares every sibling
* `maxDiff()` compares every item with all other items
* `maxDiffAhead()` compares every item with all other items that follow it
For `[1,2,3,4]`:
| Method | Comparisons performed |
|--|--|
| `maxPairDiff()` | 1-2, 2-3 and 3-4 |
| `maxDiff()` | 1-2, 1-3, 1-4, 2-1, 2-3, 2-4, 3-1, 3-2, 3-4, 4-1, 4-2 and 4-3 |
| `maxDiffAhead()` | 1-2, 1-3, 1-4, 2-3, 2-4 and 3-4 |