npm i @damianc/array-x
Note that it's
@damianc/array-x
: if you only typearray-x
, completely other package will be installed!
Observing:
Collecting:
group()
index()
pivot()
joinOneToOne()
joinOneToMany()
joinManyToOne()
takeUntil()
takeUntilReduce()
skipUntil()
skipUntilReduce()
extractSub()
uniq()
uniqAdjacent()
nth()
multiple()
last()
pluck()
pluckMultiple()
glue()
glueHeads()
glueTails()
iterator()
revIterator()
refIterator()
cyclicIterator()
echoIterator()
everyNth()
select()
Searching:
Examining:
Clustering:
chunk()
chunkByCallback()
chunkByReduce()
chunkByPattern()
chunkByGroup()
partition()
chunkReduce()
frameReduce()
zip()
zipAll()
unzip()
unzipAll()
split()
splitByPattern()
cut()
chop()
Altering:
wrapAlter()
adapt()
adaptByCallback()
random()
shuffle()
move()
insert()
overwrite()
override()
cork()
precede()
follow()
frozen()
fixed()
dwarf()
alterable()
clamped()
fold()/unfold()
sparse()
thick()
Sets:
Redefined built-ins:
reduce()
scan()
sample()
flat()
sort()
reversed()
forEach()
spreadMap()
filterMapped()
replace()
findIndexes()
includes()
mapReduce()
pop()
shift()
pull()
remove()
fill()
generate()
padLeft()
padRight()
align()
join()
matchSome()
matchEvery()
toString()
toJSON()
frame()
audit()
auditChunks()
audit()
vs.auditChunks()
expandTo()
expandToLength()
Iteration:
Min and Max:
Difference:
minPairDiff()
maxPairDiff()
minDiff()
maxDiff()
minDiffAhead()
maxDiffAhead()
maxPairDiff()
vs.maxDiff()
vs.maxDiffAhead()
Enriches array by additional methods:
on(type, handler)
- adds a handler for given actionoff(type, handler)
- removes a handler for given actioncleanListeners(type)
- removes all handlers for given actioncleanAllListeners()
- removes all handlers for all actionsunobserve()
- makes array is no longer observable
type
parameter is one of the following:push
orpop
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
[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' }]
// }
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' ]
}
*/
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' ]
// }
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' }
]
*/
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: [] }
]
*/
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: [] }
]
*/
[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]
// 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
)
// []
[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)
// []
[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
)
// []
[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]] ]
[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}]
[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 }
// ]
const arr = [1, 2, 2, 3, 3, 2, 1, 1];
arr.x.uniq()
// [1, 2, 3]
arr.x.uniqAdjacent()
// [1, 2, 3, 2, 1]
[1,2,3,4].x.nth(0)
// 1
[1,2,3,4].x.nth(-1)
// 4
['foo', 'bar', 'baz', 'quux'].x.multiple([1,3])
// ['bar', 'quux']
[1,2,3,4].x.last()
// 4
[10].x.last()
// 10
[].x.last()
// undefined
[
{ 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]
[
{ 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] ]
[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]
[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]
[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]
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]
// []
const revIter = [1,2,3,4].x.revIterator();
[...revIter]
// [4,3,2,1]
[...revIter]
// []
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'
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'
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'
[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]
[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]
[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
[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)
// []
[5, 6, 7, 8].x.localizeMin()
// [0, 5]
['xxxx', 'xx', 'xxx'].x.localizeMin(str => str.length)
// [1, 'xx']
[].x.localizeMin()
// null
[5, 6, 7, 8].x.localizeMax()
// [3, 8]
['xxxx', 'xx', 'xxx'].x.localizeMax(str => str.length)
// [0, 'xxxx']
[].x.localizeMax()
// null
[5, 6, 7, 8].x.min()
// 5
['xxxx', 'xx', 'xxx'].x.min(str => str.length)
// 2
[].x.min()
// null
[5, 6, 7, 8].x.findMin()
// 5
['xxxx', 'xx', 'xxx'].x.findMin(str => str.length)
// 'xx'
[].x.findMin()
// null
[5, 6, 7, 8].x.findMinIndex()
// 0
['xxxx', 'xx', 'xxx'].x.findMinIndex(str => str.length)
// 1
[].x.findMinIndex()
// -1
[5, 6, 7, 8].x.max()
// 8
['xxxx', 'xx', 'xxx'].x.max(str => str.length)
// 4
[].x.max()
// null
[5, 6, 7, 8].x.findMax()
// 8
['xxxx', 'xx', 'xxx'].x.findMax(str => str.length)
// 'xxxx'
[].x.findMax()
// null
[5, 6, 7, 8].x.findMaxIndex()
// 3
['xxxx', 'xx', 'xxx'].x.findMaxIndex(str => str.length)
// 0
[].x.findMaxIndex()
// -1
[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
['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 }
[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
[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
[1,2,3,4].x.sum()
// 10
['foo', 'bar', 'baz'].x.sum(str => str.length)
// 9
[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)
// []
// 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] ]
[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] ]
[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] ]
['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
orfalse
:
[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] ]
The partitioner should return a number. If you want to handle strings, use chunkByGroup()
.
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
[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
[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]
['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] ]
['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] ]
[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] ]
[ ['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] ]
[ ['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'] ]
[ ['a', 1], ['b', 2, 'foo'] ].x.unzip()
// [ ['a', 'b'], [1, 2] ]
[ ['a', 1], ['b', 2, 'foo'] ].x.unzipAll()
// [ ['a', 'b'], [1, 2], [undefined, 'foo'] ]
[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]
[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] ]
[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] ]
[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] ]
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 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]
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]
[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)
[1,2,3,4].x.shuffle()
// [4,2,3,1] (for example)
// 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]
[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]
[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]
[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]
[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]
[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]
[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]
[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]
Disable addition, removal and update.
alias:
readonly()
orreadOnly()
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]
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]
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]
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]
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]
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' }
[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]
[1,2,,,,3,,,4].x.thick()
// [1,2,3,4]
[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]
[1,2,3,4].x.intersection([3,4,5,6])
// [3,4]
[1,2,3,4].x.diff([3,4,5,6])
// [1,2]
[1,2,3,4].x.symDiff([3,4,5,6])
// [1,2,5,6]
[1,2,3,4].x.complement([3,4,5,6])
// [5,6]
[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)
[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']
[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] ]
- 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]
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 byfoo
key in ascending order+foo
to sort byfoo
key in ascending order-foo
to sort byfoo
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']
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
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
[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]
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']
[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]
[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()
// []
[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
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
)
// ['<div>', '<span>']
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]
const arr = [1,2,3,4,5,6];
arr.x.pop(2);
// returns removed items: [5,6]
arr
// [1,2,3,4]
const arr = [1,2,3,4,5,6];
arr.x.shift(2);
// returns removed items: [1,2]
arr
// [3,4,5,6]
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]
- 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]
[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]
[].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]
alias:
pad()
[1,2].x.padLeft(4, 0)
// [0,0,1,2]
[1,2].x.pad(4, 0)
// [0,0,1,2]
[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]
[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]
[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'
[1,2,3,4,5,6].x.matchSome(
x => x % 2 === 0,
x => x >= 3
)
// [2,3,4,5,6]
[1,2,3,4,5,6].x.matchEvery(
x => x % 2 === 0,
x => x >= 3
)
// [4,6]
- 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'
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"}'
[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] ]
- 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
- 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
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
To compute another value,
N
last items are taken.
N
equals value of theprevs
parameter if exists, otherwise it looks for arity of thecb
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]
To compute another value,
N
last items are taken.
N
equals value of theprevs
parameter if exists, otherwise it looks for arity of thecb
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]
[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]
[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]
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
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
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
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
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
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()
compares every siblingmaxDiff()
compares every item with all other itemsmaxDiffAhead()
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 |