{"id":21766589,"url":"https://github.com/damianc/array-x","last_synced_at":"2026-05-06T23:41:43.583Z","repository":{"id":58876214,"uuid":"532293785","full_name":"damianc/array-x","owner":"damianc","description":"Set of additional array methods.","archived":false,"fork":false,"pushed_at":"2022-11-25T18:44:05.000Z","size":307,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-26T02:19:55.809Z","etag":null,"topics":["array","array-methods","arrays","counting","data-grouping","javascript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/damianc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-03T15:07:38.000Z","updated_at":"2022-10-04T19:31:09.000Z","dependencies_parsed_at":"2023-01-22T02:36:02.241Z","dependency_job_id":null,"html_url":"https://github.com/damianc/array-x","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damianc%2Farray-x","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damianc%2Farray-x/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damianc%2Farray-x/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damianc%2Farray-x/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/damianc","download_url":"https://codeload.github.com/damianc/array-x/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244742342,"owners_count":20502457,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","array-methods","arrays","counting","data-grouping","javascript"],"created_at":"2024-11-26T13:18:11.347Z","updated_at":"2026-05-06T23:41:38.562Z","avatar_url":"https://github.com/damianc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `array-x`\n\n## Installation\n\n```\nnpm i @damianc/array-x\n```\n\n\u003e Note that it's `@damianc/array-x`: if you only type `array-x`, completely other package will be installed!\n\nObserving:\n- [`observe()`](#observe)\n\nCollecting:\n- [`group()`](#grouplabelFactory)\n- [`index()`](#indexkeySelector-valueSelector--null-fallbackKey--__unindexed)\n- [`pivot()`](#pivotindexKey--null-valueSelector--null-keyMapper--null)\n- [`joinOneToOne()`](#joinOneToOnesource-targetKey-sourceKey-targetNewKey-sourceValueSelector--null)\n- [`joinOneToMany()`](#joinOneToManysource-targetKey-sourceKey-targetNewKey-sourceValueSelector--null)\n- [`joinManyToOne()`](#joinManyToOnesource-targetKey-sourceKey-targetNewKey-sourceValueSelector--null)\n- [`takeUntil()`](#takeUntilitemOrMatcher-inclusive--true)\n- [`takeUntilReduce()`](#takeUntilReduceaccTester-reducer-reducerInit-inclusive--true)\n- [`skipUntil()`](#skipUntilitemOrMatcher-inclusive--true)\n- [`skipUntilReduce()`](#skipUntilReduceaccTester-reducer-reducerInit-inclusive--true)\n- [`extractSub()`](#extractSubpattern-multi--true-overlay--true)\n- [`uniq()`](#uniqselector)\n- [`uniqAdjacent()`](#uniqAdjacentselector)\n- [`nth()`](#nthindex)\n- [`multiple()`](#multipleindexes)\n- [`last()`](#last)\n- [`pluck()`](#pluckpath)\n- [`pluckMultiple()`](#pluckMultiplepaths)\n- [`glue()`](#glueotherArray---headSize-tailSize--headSize)\n- [`glueHeads()`](#glueHeadsotherArray---leftSize-rightSize--leftSize)\n- [`glueTails()`](#glueTailsotherArray---leftSize-rightSize--leftSize)\n- [`iterator()`](#iterator)\n- [`revIterator()`](#revIterator)\n- [`refIterator()`](#refIteratorrefKey-nextKey-initItemIdx--0)\n- [`cyclicIterator()`](#cyclicIterator)\n- [`echoIterator()`](#echoIteratorsticky--false)\n- [`everyNth()`](#everyNthn--1-from--0-to)\n- [`select()`](#selectfrom--0-to---1-step--1)\n\nSearching:\n- [`localize()`](#localizeitemOrMatcher)\n- [`localizeAll()`](#localizeAllitemOrMatcher)\n- [`localizeMin()`](#localizeMinmapper)\n- [`localizeMax()`](#localizeMaxmapper)\n\nExamining:\n- [`count()`](#countitemOrMatcher)\n- [`countAll()`](#countAlllabelFactory)\n- [`avg()`](#avgmapper)\n- [`wavg()`](#wavgweightsArrOrWeightSelector-selector)\n- [`sum()`](#summapper)\n\nClustering:\n- [`chunk()`](#chunksize-rejectStickingTail--false)\n- [`chunkByCallback()`](#chunkByCallbackcb-matchedItemOpening--true)\n- [`chunkByReduce()`](#chunkByReducereducer-init--null-tester--null)\n- [`chunkByPattern()`](#chunkByPatternsizes-rejectStickingTail--false)\n- [`chunkByGroup()`](#chunkByGroupgrouper--item--0)\n- [`partition()`](#partitionpartitioner--item--0)\n- [`chunkReduce()`](#chunkReducesize-reducer--null-init--null-rejectStickingTail--false)\n- [`frameReduce()`](#frameReducesize-reducer--null-init--null-rejectStickingTail--false)\n- [`zip()`](#zipotherArrays)\n- [`zipAll()`](#zipAllotherArrays)\n- [`unzip()`](#unzip)\n- [`unzipAll()`](#unzipAll)\n- [`split()`](#splitmatcher--null)\n- [`splitByPattern()`](#splitByPatterntake-skip-from--null-rejectStickingTail--false)\n- [`cut()`](#cutindex)\n- [`chop()`](#chopcuts)\n\nAltering:\n- [`wrapAlter()`](#wrapAlterprocessor)\n- [`adapt()`](#adaptotherArray---adaptations--1)\n- [`adaptByCallback()`](#adaptByCallbackotherArray---tester--null-adaptations--1-lazy--false)\n- [`random()`](#randomcount--1)\n- [`shuffle()`](#shuffle)\n- [`move()`](#movesourceIndex-count-targetIndex--0)\n- [`insert()`](#insertindex-items)\n- [`overwrite()`](#overwriteindex-items)\n- [`override()`](#overrideindex-items)\n- [`cork()`](#corkvalue)\n- [`precede()`](#precedeappendix---fillWithUndefined--false)\n- [`follow()`](#followappendix---fillWithUndefined--false)\n- [`frozen()`](#frozen)\n- [`fixed()`](#fixed)\n- [`dwarf()`](#dwarf)\n- [`alterable()`](#alterable)\n- [`clamped()`](#clampedmin-max)\n- [`fold()/unfold()`](#foldcoverSelectorunfoldindex)\n- [`sparse()`](#sparseslots--1)\n- [`thick()`](#thick)\n\nSets:\n- [`union()`](#unionotherSet--)\n- [`intersection()`](#intersectionotherSet--)\n- [`diff()`](#diffotherSet--)\n- [`symDiff()`](#symDiffotherSet--)\n- [`complement()`](#complementotherSet--)\n\nRedefined built-ins:\n- [`reduce()`](#reducereducer-finalizer--null-init)\n- [`scan()`](#scanreducer-init--null)\n- [`sample()`](#samplesize-step--1)\n- [`flat()`](#flatlevel---1)\n- [`sort()`](#sortcomparators)\n- [`reversed()`](#reversed)\n- [`forEach()`](#forEachcb)\n- [`spreadMap()`](#spreadMapmapper)\n- [`filterMapped()`](#filterMappedmapper--null-filter--null)\n- [`replace()`](#replacematcher--x--x-replacer--x--x)\n- [`findIndexes()`](#findIndexesmatcher--null)\n- [`includes()`](#includesmatcher--null)\n- [`mapReduce()`](#mapReducechunkSize--2-reducer--null-init--null-rejectStickingTail--false)\n- [`pop()`](#popn--1)\n- [`shift()`](#shiftn--1)\n- [`pull()`](#pullindex)\n- [`remove()`](#removevaluesOrCb)\n- [`fill()`](#fillvalueOrGenerator)\n- [`generate()`](#generatelength-valueGenerator)\n- [`padLeft()`](#padLeftmaxLength-value)\n- [`padRight()`](#padRightmaxLength-value)\n- [`align()`](#alignlength-filling-padSide--left-cutSide--right)\n- [`join()`](#joinseparator)\n- [`matchSome()`](#matchSometests)\n- [`matchEvery()`](#matchEverytests)\n- [`toString()`](#toStringitemMapper--null-separator--null-emptyMsg--null)\n- [`toJSON()`](#toJSONmapper--null)\n- [`frame()`](#framesize-rejectIncomplete--true)\n- [`audit()`](#audittester-frameSize--2)\n- [`auditChunks()`](#auditChunkstester-chunkSize--2-rejectSticking--true)\n- [`audit()` vs. `auditChunks()`](#audit-vs-auditChunks)\n- [`expandTo()`](#expandTotarget-cb-prevs-init--0-includeSticking--false-maxIters--32)\n- [`expandToLength()`](#expandToLengthlength-cb-prevs-init--0)\n\nIteration:\n- [`forEachChunk()`](#forEachChunkchunkSize-cb)\n- [`forEachFrame()`](#forEachFrameblockSize-cb-fullBlocksOnly--false)\n\nMin and Max:\n- [`min()`](#minmapper)\n- [`max()`](#maxmapper)\n- [`findMin()`](#findMinmapper)\n- [`findMax()`](#findMaxmapper)\n- [`findMinIndex()`](#findMinIndexmapper)\n- [`findMaxIndex()`](#findMaxIndexmapper)\n\nDifference:\n\n- [`minPairDiff()`](#minPairDiffcomparer--null)\n- [`maxPairDiff()`](#maxPairDiffcomparer--null)\n- [`minDiff()`](#minDiffcomparer--null)\n- [`maxDiff()`](#maxDiffcomparer--null)\n- [`minDiffAhead()`](#minDiffAheadcomparer--null)\n- [`maxDiffAhead()`](#maxDiffAheadcomparer--null)\n- [`maxPairDiff()` vs. `maxDiff()` vs. `maxDiffAhead()`](#maxPairDiff-vs-maxDiff-vs-maxDiffAhead)\n\n## `observe()`\n\nEnriches array by additional methods:\n\n- `on(type, handler)` - adds a handler for given action\n- `off(type, handler)` - removes a handler for given action\n- `cleanListeners(type)` - removes all handlers for given action\n- `cleanAllListeners()` - removes all handlers for all actions\n- `unobserve()` - makes array is no longer observable\n\n\u003e `type` parameter is one of the following: `push` or `pop`\n\n```\nconst arr = [1,2,3,4];\narr.x.observable();\n\narr.on('push', (newArray, itemsAdded, newLength) =\u003e {\n  console.log('added new item(s):', itemsAdded);\n});\n\narr.push(5,6);\n// added new item(s): [5,6]\n```\n\n```\nconst arr = [1,2,3,4];\narr.x.observable();\n\narr.on('pop', (newArray, itemRemoved) =\u003e {\n  console.log('removed item:', itemRemoved);\n});\narr.on('pop', (newArray, itemRemoved) =\u003e {\n  console.log('item has been removed:', itemRemoved);\n});\n\narr.pop();\n// removed item: 4\n// item has been removed: 4\n```\n\n## `group(labelFactory...)`\n\n```\n[1, 2, 3, 4].x.group(x =\u003e x % 2 === 0 ? 'even' : 'odd')\n// {\n//   odd: [1, 3],\n//   even: [2, 4]\n// }\n\n[1, 2, 30, 45].x.group(\n  x =\u003e x % 2 === 0 ? 'even' : 'odd',\n  x =\u003e x \u003e 9 ? 'multidigit' : 'digit'\n)\n// {\n//   odd: [1, 45],\n//   even: [2, 30],\n//   digit: [1, 2],\n//   multidigit: [30, 45]\n// }\n\n[1, 2, 30, 45].x.group(\n  x =\u003e x % 2 === 0 ? 'even' : 'odd',\n  x =\u003e x \u003e 9 ? 'multidigit' : null\n)\n// {\n//   odd: [1, 45],\n//   even: [2, 30],\n//   multidigit: [30, 45]\n// }\n\n[\n  { name: 'Mark', city: 'LA' },\n  { name: 'John', city: 'NY' },\n  { name: 'Adam', city: 'LA' }\n].x.group(u =\u003e u.city)\n// {\n//   LA: [{ name: 'Mark', city: 'LA' }, { name: 'Adam', city: 'LA' }],\n//   NY: [{ name: 'John', city: 'NY' }]\n// }\n```\n\n## `index(keySelector, valueSelector = null, fallbackKey = '__unindexed')`\n\n- `keySelector`: `string` or `(item: any, index: number) =\u003e string`\n- `valueSelector`: `string` or `(item: any, index: number) =\u003e string`\n- `fallbackKey`: `string`\n\n```\n[\n  { id: 1, name: 'John', city: 'NY' },\n  { id: 2, name: 'Mark', city: 'LA' },\n  { id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }\n].x.index('city');\n\n/*\n{\n  NY: [ { id: 1, name: 'John', city: 'NY' } ],\n  LA: [\n    { id: 2, name: 'Mark', city: 'LA' },\n    { id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }\n  ]\n}\n*/\n```\n\n```\n[\n  { id: 1, name: 'John', city: 'NY' },\n  { id: 2, name: 'Mark', city: 'LA' },\n  { id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }\n].x.index(\n  u =\u003e u.city.toLowerCase(),\n  'id'\n);\n\n/*\n{\n  ny: [ 1 ],\n  la: [ 2, 3 ]\n}\n*/\n```\n\n```\n[\n  { id: 1, name: 'John', city: 'NY' },\n  { id: 2, name: 'Mark', city: 'LA' },\n  { id: 3, name: 'Adam', city: 'LA', extraKey: 'foo' }\n].x.index('extraKey', 'name');\n\n/*\n{\n  foo: [ 'Adam' ],\n  __unindexed: [ 'John', 'Mark' ]\n}\n*/\n```\n\n## `pivot(indexKey = null, valueSelector = null, keyMapper = null)`\n\n```\nconst posts = [\n  { id: 120, title: 'Post A', author: 'John' },\n  { id: 121, title: 'Post B', author: 'Mark' },\n  { id: 122, title: 'Post C', author: 'John' }\n];\n\nposts.x.pivot('author', 'title')\n// {\n//   John: ['Post A', 'Post C'],\n//   Mark: ['Post B']\n// }\n```\n\n```\nconst posts = [\n  { id: 120, title: 'Post A', author: 'John' },\n  { id: 121, title: 'Post B', author: 'Mark' },\n  { id: 122, title: 'Post C', author: 'John' }\n];\n\nposts.x.pivot(\n  'author',\n  p =\u003e `[#${p.id}] ${p.title}`,\n  a =\u003e a.toLowerCase()\n)\n// {\n//   john: ['[#120] Post A', '[#122] Post C'],\n//   mark: ['[#121] Post B']\n// }\n```\n\n```\nconst posts = [\n  { id: 120, title: 'Post A', author: 'John' },\n  { id: 121, title: 'Post B', author: 'Mark' },\n  { id: 122, title: 'Post C', author: 'John' }\n];\n\nposts.x.pivot('author')\n// {\n//   John: [\n//     { id: 120, title: 'Post A' },\n//     { id: 122, title: 'Post C' }\n//   ],\n//   Mark: [\n//     { id: 121, title: 'Post B' }\n//   ]\n// }\n\nposts.x.pivot('author', p =\u003e p)\n// {\n//   John: [\n//     { id: 120, title: 'Post A', author: 'John' },\n//     { id: 122, title: 'Post C', author: 'John' }\n//   ],\n//   Mark: [\n//     { id: 121, title: 'Post B', author: 'Mark' }\n//   ]\n// }\n```\n\n```\nconst arr = [\n  [1, 'foo'], [1, 'bar'], [2, 'baz']\n];\n\narr.x.pivot(0, 1)\n// {\n//   1: [ 'foo', 'bar' ],\n//   2: [ 'baz' ]\n// }\n```\n\n## `joinOneToOne(source, targetKey, sourceKey, targetNewKey, sourceValueSelector = null)`\n\n```\nconst posts = [\n  { id: 1, title: 'Post 1', authorId: 100 },\n  { id: 2, title: 'Post 2', authorId: 102 }\n];\n\nconst authors = [\n  { id: 100, name: 'John' },\n  { id: 101, name: 'Mark' },\n  { id: 102, name: 'Adam' }\n];\n\n\nposts.x.joinOneToOne(\n  authors,\n  'authorId',\n  'id',\n  'author',\n  author =\u003e author.name\n)\n\n/*\n[\n  { id: 1, title: 'Post 1', authorId: 100, author: 'John' },\n  { id: 2, title: 'Post 2', authorId: 102, author: 'Adam' }\n]\n*/\n```\n\n## `joinOneToMany(source, targetKey, sourceKey, targetNewKey, sourceValueSelector = null)`\n\n```\nconst authors = [\n  { id: 100, name: 'John' },\n  { id: 102, name: 'Adam' },\n  { id: 101, name: 'Mark' }\n];\n\nconst posts = [\n  { id: 1, title: 'Post 1 by John', authorId: 100 },\n  { id: 2, title: 'Post 1 by Adam', authorId: 102 },\n  { id: 3, title: 'Post 2 by Adam', authorId: 102 },\n  { id: 4, title: 'Post 2 by John', authorId: 100 }\n];\n\n\nauthors.x.joinOneToMany(\n  posts,\n  'id',\n  'authorId',\n  'posts',\n  post =\u003e post.title\n)\n\n/*\n[\n  { id: 100, name: 'John', posts: ['Post 1 by John', 'Post 2 by John'] },\n  { id: 102, name: 'Adam', posts: ['Post 1 by Adam', 'Post 2 by Adam'] },\n  { id: 101, name: 'Mark', posts: [] }\n]\n*/\n```\n\n## `joinManyToOne(source, targetKey, sourceKey, targetNewKey, sourceValueSelector = null)`\n\n```\n const posts = [\n  { id: 1, title: 'Post 1', tagsIds: [10,20] },\n  { id: 2, title: 'Post 2', tagsIds: [20,40] },\n  { id: 3, title: 'Post 3', tagsIds: [] },\n  { id: 4, title: 'Post 4' },\n];\n\nconst tags = [\n  { id: 10, name: 'Tag A' },\n  { id: 20, name: 'Tag B' },\n  { id: 30, name: 'Tag C' },\n  { id: 40, name: 'Tag D' }\n];\n\n\nposts.x.joinManyToOne(\n  tags,\n  'tagsIds',\n  'id',\n  'tags',\n  tag =\u003e tag.name\n)\n\n/*\n[\n  { id: 1, title: 'Post 1', tagsIds: [10, 20], tags: ['Tag A', 'Tag B'] },\n  { id: 2, title: 'Post 2', tagsIds: [20, 40], tags: ['Tag B', 'Tag D'] },\n  { id: 3, title: 'Post 3', tagsIds: [], tags: [] },\n  { id: 4, title: 'Post 4', tags: [] }\n]\n*/\n```\n\n## `takeUntil(itemOrMatcher, inclusive = true)`\n\n```\n[1, 2, 3, 4, 5, 6].x.takeUntil(4)\n// [1, 2, 3, 4]\n\n[1, 2, 3, 4, 5, 6].x.takeUntil(4, false)\n// [1, 2, 3]\n\n[1, 3, 2, 4].x.takeUntil(x =\u003e x % 2 === 0)\n// [1, 3, 2]\n\n[1, 3, 2, 4].x.takeUntil(x =\u003e x % 2 === 0, false)\n// [1, 3]\n\n[1, 2, 3, 4, 5, 6].x.takeUntil(10)\n// [1, 2, 3, 4, 5, 6]\n```\n\n## `takeUntilReduce(accTester, reducer, reducerInit, inclusive = true)`\n\n```\n// collect numbers until their average equals 5\n\nconst res = [6, 5, 4, 3, 2, 1].x.takeUntilReduce(\n  (acc) =\u003e acc.x.avg() === 5,\n  (a, b) =\u003e [...a, b],\n  []\n);\n// [6, 5, 4]\n```\n\n```\n[1, 2, 3, 4, 5, 6].x.takeUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0\n)\n// [1, 2, 3, 4]\n\n[1, 2, 3, 4, 5, 6].x.takeUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0,\n  false\n)\n// [1, 2, 3]\n\n[10, 20, 30, 40].x.takeUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0\n)\n// [10]\n\n[10, 20, 30, 40].x.takeUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0,\n  false\n)\n// []\n```\n\n## `skipUntil(itemOrMatcher, inclusive = true)`\n\n```\n[1, 2, 3, 4, 5, 6].x.skipUntil(4)\n// [4, 5, 6]\n\n[1, 2, 3, 4, 5, 6].x.skipUntil(4, false)\n// [5, 6]\n\n[1, 3, 2, 4].x.skipUntil(x =\u003e x % 2 === 0)\n// [2, 4]\n\n[1, 3, 2, 4].x.skipUntil(x =\u003e x % 2 === 0, false)\n// [4]\n\n[1, 2, 3, 4, 5, 6].x.skipUntil(10)\n// []\n```\n\n## `skipUntilReduce(accTester, reducer, reducerInit, inclusive = true)`\n\n```\n[1, 2, 3, 4, 5, 6].x.skipUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0\n)\n// [4, 5, 6]\n\n[1, 2, 3, 4, 5, 6].x.skipUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0,\n  false\n)\n// [5, 6]\n\n[10, 20, 30, 40].x.skipUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0\n)\n// [10, 20, 30, 40]\n\n[10, 20, 30, 40].x.skipUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0,\n  false\n)\n// [20, 30, 40]\n\n[1, 2, 1, 2].x.skipUntilReduce(\n  sum =\u003e sum \u003e= 10,\n  (a, b) =\u003e a + b,\n  0\n)\n// []\n```\n\n## `extractSub(pattern, multi = true, overlay = true)`\n\n```\n[1,2,3,4,1,2,3,4].x.extractSub(\n  [1,2]\n)\n\n// [\n//   [ 0, [1,2] ],\n//   [ 4, [1,2] ]\n// ]\n```\n\n```\nconst even = x =\u003e x % 2 === 0;\nconst odd = x =\u003e x % 2 !== 0;\n\n[1,2,3,4,1,2,3,4].x.extractSub(\n  [1,even,odd]\n)\n\n// [\n//   [ 0, [1,2,3] ],\n//   [ 4, [1,2,3] ]\n// ]\n```\n\n* get only one:\n\n```\n[1,2,3,4,1,2,3,4].x.extractSub(\n  [1,even,odd], false\n)\n\n// [ 0, [1,2,3] ]\n```\n\n```\nconst arr = [1,2,4,6,3,5];\n\narr.x.extractSub(\n  [even, even]\n)\n// [ [1,[2,4]], [2,[4,6]] ]\n\narr.x.extractSub(\n  [even,even], true, false\n)\n// [ [1,[2,4]] ]\n```\n\n## `uniq([selector])`\n\n```\n[1, 2, 3, 2, 1].x.uniq()\n// [1, 2, 3]\n\n[{x:10,y:10}, {x:10,y:20}, {x:20,y:20}, {x:20,y:10}].x.uniq(coords =\u003e coords.x)\n// [{x:10,y:10}, {x:20,y:20}]\n```\n\n## `uniqAdjacent([selector])`\n\n```\n[1, 2, 3, 2, 1].x.uniqAdjacent()\n// [1, 2, 3, 2, 1]\n\n[1, 2, 3, 3, 2, 2, 1, 1].x.uniqAdjacent()\n// [1, 2, 3, 2, 1]\n\n[\n  { x: 1, id: 100 },\n  { x: 2, id: 101 },\n  { x: 3, id: 102 },\n  { x: 3, id: 103 },\n  { x: 2, id: 104 },\n  { x: 2, id: 105 },\n  { x: 1, id: 106 },\n  { x: 1, id: 107 }\n].x.uniqAdjacent(obj =\u003e obj.x)\n// [\n//   { x: 1, id: 100 },\n//   { x: 2, id: 101 },\n//   { x: 3, id: 102 },\n//   { x: 2, id: 104 },\n//   { x: 1, id: 106 }\n// ]\n```\n\n### `uniq()` vs. `uniqAdjacent()`\n\n```\nconst arr = [1, 2, 2, 3, 3, 2, 1, 1];\n\narr.x.uniq()\n// [1, 2, 3]\n\narr.x.uniqAdjacent()\n// [1, 2, 3, 2, 1]\n```\n\n## `nth(index)`\n\n```\n[1,2,3,4].x.nth(0)\n// 1\n\n[1,2,3,4].x.nth(-1)\n// 4\n```\n\n## `multiple(indexes)`\n\n```\n['foo', 'bar', 'baz', 'quux'].x.multiple([1,3])\n// ['bar', 'quux']\n```\n\n## `last()`\n\n```\n[1,2,3,4].x.last()\n// 4\n\n[10].x.last()\n// 10\n\n[].x.last()\n// undefined\n```\n\n## `pluck(path)`\n\n```\n[\n  { foo: { bar: [1,2,{ baz: 120 }] } },\n  { foo: { bar: [3,4,{ baz: 340 }] } },\n  { foo: { bar: [5,6,{ baz: 560 }] } }\n].x.pluck('foo.bar[2].baz')\n// other working variant: pluck('foo.bar.2.baz')\n\n// [120, 340, 560]\n```\n\n```\n[\n  [1,2,3],\n  [2,3,5],\n  [3,4,7],\n  [4,5,9]\n].x.pluck(2)\n// other working variants: pluck('2'), pluck('[2]')\n\n// [3, 5, 7, 9]\n```\n\n## `pluckMultiple(...paths)`\n\n```\n[\n  { foo: { bar: [1,2,{ baz: 120 }] } },\n  { foo: { bar: [3,4,{ baz: 340 }] } },\n  { foo: { bar: [5,6,{ baz: 560 }] } }\n].x.pluckMultiple('foo.bar[0]', 'foo.bar[1]', 'foo.bar[2].baz')\n\n// [ [1,2,120], [3,4,340], [5,6,560] ]\n```\n\n```\n[\n  [1,2,3],\n  [2,3,5],\n  [3,4,7],\n  [4,5,9]\n].x.pluckMultiple(0, 2)\n\n// [ [1,3], [2,5], [3,7], [4,9] ]\n```\n\n## `glue(otherArray = [], headSize, tailSize = headSize)`\n\n```\n[1,2,3,4].x.glue([5,6,7,8], 2)\n// [1,2,7,8]\n\n[1,2,3,4].x.glue([5,6,7,8], 1, 3)\n// [1,6,7,8]\n\n[1,2,3,4].x.glue([5,6,7,8], 3, 1)\n// [1,2,3,8]\n```\n\n## `glueHeads(otherArray = [], leftSize, rightSize = leftSize)`\n\n```\n[1,2,3,4].x.glueHeads([5,6,7,8], 2)\n// [1,2,5,6]\n\n[1,2,3,4].x.glueHeads([5,6,7,8], 1, 3)\n// [1,5,6,7]\n\n[1,2,3,4].x.glueHeads([5,6,7,8], 3, 1)\n// [1,2,3,5]\n```\n\n## `glueTails(otherArray = [], leftSize, rightSize = leftSize)`\n\n```\n[1,2,3,4].x.glueTails([5,6,7,8], 2)\n// [3,4,7,8]\n\n[1,2,3,4].x.glueTails([5,6,7,8], 1, 3)\n// [4,6,7,8]\n\n[1,2,3,4].x.glueTails([5,6,7,8], 3, 1)\n// [2,3,4,8]\n```\n\n## `iterator()`\n\n```\nconst iter = [1,2,3,4].x.iterator();\n\n[...iter]\n// [1,2,3,4]\n\n[...iter]\n// []\n```\n\n```\nconst iter = [1,2,3,4].x.iterator();\n\niter.next()\n// {value: 1, done: false}\n\niter.next()\n// {value: 2, done: false}\n\n[...iter]\n// [3,4]\n\niter.next()\n// {value: undefined, done: true}\n\n[...iter]\n// []\n```\n\n## `revIterator()`\n\n```\nconst revIter = [1,2,3,4].x.revIterator();\n\n[...revIter]\n// [4,3,2,1]\n\n[...revIter]\n// []\n```\n\n## `refIterator(refKey, nextKey, initItemIdx = 0)`\n\n```\nconst blocks = [\n  { id: 1, next: 2, data: 'A' },\n  { id: 3, next: 4, data: 'C' },\n  { id: 4, data: 'D' },\n  { id: 2, next: 3, data: 'B' }\n];\n\nconst chain = blocks.x.refIterator('id', 'next');\nconst path = [...chain].map(b =\u003e b.data).join(' -\u003e ');\n\npath\n// 'A -\u003e B -\u003e C -\u003e D'\n```\n\n```\nconst blocks = [\n  { id: 1, next: 2, data: 'A' },\n  { id: 3, next: 4, data: 'C' },\n  { id: 4, data: 'D' },\n  { id: 2, next: 3, data: 'B' }\n];\n\nconst chain = blocks.x.refIterator('id', 'next', 1);\nconst path = [...chain].map(b =\u003e b.data).join(' -\u003e ');\n\npath\n// 'C -\u003e D'\n```\n\n```\nconst blocks = [\n  { id: 1, next: 20, data: 'A' },\n  { id: 2, next: 3, data: 'B' }\n];\n\nconst chain = blocks.x.refIterator('id', 'next');\nconst path = [...chain].map(b =\u003e b.data).join(' -\u003e ');\n\npath\n// 'A'\n```\n\n## `cyclicIterator()`\n\n```\nconst ci = [1,2,3,4].x.cyclicIterator();\nconst res = [];\n\nfor (let i = 1; i \u003c= 10; i++) {\n  res.push(ci.current);\n  ci.next();\n}\n\nconsole.log(\n  res.join('-')\n);\n// '1-2-3-4-1-2-3-4-1-2'\n```\n\n## `echoIterator(sticky = false)`\n\n```\nconst echit = [1,2,3,4].x.echoIterator();\n\nlet chain = echit.current;\nfor (\n  let i = 1, _ = echit.next();\n  i++ \u003c 12;\n  echit.next()\n) chain += ' -\u003e ' + echit.current;\n\nchain\n// '1 -\u003e 2 -\u003e 3 -\u003e 4 -\u003e 3 -\u003e 2 -\u003e 1 -\u003e 2 -\u003e 3 -\u003e 4 -\u003e 3 -\u003e 2'\n```\n\n```\nconst echit = [1,2,3,4].x.echoIterator(true);\n\nlet chain = echit.current;\nfor (\n  let i = 1, _ = echit.next();\n  i++ \u003c 12;\n  echit.next()\n) chain += ' -\u003e ' + echit.current;\n\nchain\n// '1 -\u003e 2 -\u003e 3 -\u003e 4 -\u003e 4 -\u003e 3 -\u003e 2 -\u003e 1 -\u003e 1 -\u003e 2 -\u003e 3 -\u003e 4'\n```\n\n## `everyNth(n = 1, from = 0, to)`\n\n```\n[1,2,3,4,5,6].x.everyNth(2)\n// [1,3,5]\n\n[1,2,3,4,5,6].x.everyNth(2, 1)\n// [2,4,6]\n\n[1,2,3,4,5,6].x.everyNth(2, -3)\n// [4,6]\n\n[1,2,3,4,5,6].x.everyNth(2, -5)\n// [2,4,6]\n\n[1,2,3,4,5,6].x.everyNth(2, -5, -2)\n// [2,4]\n```\n\n## `select(from = 0, to = -1, step = 1)`\n\n```\n[1,2,3,4,5,6].x.select(2,4)\n// [3,4,5]\n\n[1,2,3,4,5,6].x.select(2)\n// [3,4,5,6]\n\n[1,2,3,4,5,6].x.select(-2)\n// [5,6]\n\n[1,2,3,4,5,6].x.select(2,-2)\n// [3,4,5]\n\n[1,2,3,4,5,6].x.select(-3,-2)\n// [4,5]\n\n[1,2,3,4,5,6].x.select(0,-1,2)\n// [1,3,5]\n\n[1,2,3,4,5,6].x.select(1,-1,2)\n// [2,4,6]\n```\n\n## `localize(itemOrMatcher)`\n\n```\n[10, 20, 30, 40].x.localize(20)\n// [1, 20]\n\n['x', 'xx', 'xxx', 'xx', 'x'].x.localize(str =\u003e str.length \u003e= 3)\n// [2, 'xxx']\n\n[1, 2, 3, 4].x.localize(20)\n// null\n```\n\n## `localizeAll(itemOrMatcher)`\n\n```\n[10, 20, 30, 40].x.localizeAll(20)\n// [[1, 20]]\n\n[10, 20, 10, 20].x.localizeAll(20)\n// [[1, 20], [3, 20]]\n\n['x', 'xx', 'xxx'].localizeAll(str =\u003e str.length \u003e= 2)\n// [[1, 'xx'], [2, 'xxx']]\n\n[1, 2, 3, 4].x.localizeAll(20)\n// []\n```\n\n## `localizeMin([mapper])`\n\n```\n[5, 6, 7, 8].x.localizeMin()\n// [0, 5]\n\n['xxxx', 'xx', 'xxx'].x.localizeMin(str =\u003e str.length)\n// [1, 'xx']\n\n[].x.localizeMin()\n// null\n```\n\n## `localizeMax([mapper])`\n\n```\n[5, 6, 7, 8].x.localizeMax()\n// [3, 8]\n\n['xxxx', 'xx', 'xxx'].x.localizeMax(str =\u003e str.length)\n// [0, 'xxxx']\n\n[].x.localizeMax()\n// null\n```\n\n## `min([mapper])`\n\n```\n[5, 6, 7, 8].x.min()\n// 5\n\n['xxxx', 'xx', 'xxx'].x.min(str =\u003e str.length)\n// 2\n\n[].x.min()\n// null\n```\n\n## `findMin([mapper])`\n\n```\n[5, 6, 7, 8].x.findMin()\n// 5\n\n['xxxx', 'xx', 'xxx'].x.findMin(str =\u003e str.length)\n// 'xx'\n\n[].x.findMin()\n// null\n```\n\n## `findMinIndex([mapper])`\n\n```\n[5, 6, 7, 8].x.findMinIndex()\n// 0\n\n['xxxx', 'xx', 'xxx'].x.findMinIndex(str =\u003e str.length)\n// 1\n\n[].x.findMinIndex()\n// -1\n```\n\n## `max([mapper])`\n\n```\n[5, 6, 7, 8].x.max()\n// 8\n\n['xxxx', 'xx', 'xxx'].x.max(str =\u003e str.length)\n// 4\n\n[].x.max()\n// null\n```\n\n## `findMax([mapper])`\n\n```\n[5, 6, 7, 8].x.findMax()\n// 8\n\n['xxxx', 'xx', 'xxx'].x.findMax(str =\u003e str.length)\n// 'xxxx'\n\n[].x.findMax()\n// null\n```\n\n## `findMaxIndex([mapper])`\n\n```\n[5, 6, 7, 8].x.findMaxIndex()\n// 3\n\n['xxxx', 'xx', 'xxx'].x.findMaxIndex(str =\u003e str.length)\n// 0\n\n[].x.findMaxIndex()\n// -1\n```\n\n## `count(itemOrMatcher)`\n\n```\n[10, 20, 10, 20].x.count(20)\n// 2\n\n[1, 2, 3, 4, 5, 6].x.count(x =\u003e x % 2 === 0)\n// 3\n\n['x', 'xx', 'xxx'].x.count(str =\u003e str.length \u003e= 2)\n// 2\n\n[1, 2, 3, 4].count(20)\n// 0\n```\n\n## `countAll([labelFactory...])`\n\n```\n['foo', 'bar', 'foo', 'baz'].x.countAll()\n// { foo: 2, bar: 1, baz: 1 }\n\n['xx', 'xxx', 'xx'].x.countAll(str =\u003e `ofLength${str.length}`)\n// { ofLength2: 2, ofLength3: 1 }\n\n[1, 2, 3, 4, 5, 6].x.countAll(\n  x =\u003e x % 2 === 0 ? 'even' : 'odd'\n)\n// { even: 3, odd: 3 }\n\n[1, 23, 4, 56, 7, 89].x.countAll(\n  x =\u003e x % 2 === 0 ? 'even' : 'odd',\n  x =\u003e x \u003e 9 ? 'multidigit' : 'digit'\n)\n// { even: 2, odd: 4, multidigit: 3, digit: 3  }\n\n[1, 23, 4, 56, 7, 89].x.countAll(\n  x =\u003e x % 2 === 0 ? 'even' : 'odd',\n  x =\u003e x \u003e 9 ? 'multidigit' : null\n)\n// { even: 2, odd: 4, multidigit: 3  }\n\n[\n  { name: 'Mark', city: 'LA' },\n  { name: 'John', city: 'NY' },\n  { name: 'Adam', city: 'LA' }\n].x.countAll(u =\u003e u.city)\n// { LA: 2, NY: 1 }\n```\n\n## `avg([mapper])`\n\n```\n[1, 2, 3, 4].x.avg()\n// 2.5\n\n['x', 'xx', 'xxx'].x.avg(str =\u003e str.length)\n// 2\n\n[\n  { examId: 120, mark: 5 },\n  { examId: 121, mark: 4 }\n].x.avg(exam =\u003e exam.mark)\n// 4.5\n\n[].x.avg()\n// null\n```\n\n## `wavg(weightsArrOrWeightSelector, [selector])`\n\n```\n[4].x.wavg([2])\n// 4\n// (4 * 2) / 2 -\u003e 8 / 2\n\n[3, 3].x.wavg([3, 2])\n// 3\n// (3 * 3 + 3 * 2) / (3 + 2) -\u003e (9 + 6) / 5 -\u003e 15 / 5\n\n[\n  { examId: 120, mark: 5 },\n  { examId: 121, mark: 4 },\n  { examId: 122, mark: 4.5 },\n  { examId: 123, mark: 5.5 },\n  { examId: 124, mark: 5 }\n].x.wavg([1, 1, 3, 2, 4], exam =\u003e exam.mark)\n// 4.863636363636363\n// (5 + 4 + 4.5 * 3 + 5.5 * 2 + 5 * 4) / (1 + 1 + 3 + 2 + 4)\n// (5 + 4 + 13.5 + 11 + 20) / 11 -\u003e 53.5 / 11 -\u003e 4.863636363636363\n\n[\n  { examId: 120, examWeight: 1, mark: 5 },\n  { examId: 121, examWeight: 1, mark: 4 },\n  { examId: 122, examWeight: 3, mark: 4.5 },\n  { examId: 123, examWeight: 2, mark: 5.5 },\n  { examId: 124, examWeight: 4, mark: 5 }\n].x.wavg(\n  exam =\u003e exam.examWeight,\n  exam =\u003e exam.mark\n)\n// 4.863636363636363\n// as above\n```\n\n## `sum([mapper])`\n\n```\n[1,2,3,4].x.sum()\n// 10\n\n['foo', 'bar', 'baz'].x.sum(str =\u003e str.length)\n// 9\n```\n\n## `chunk(size, rejectStickingTail = false)`\n\n```\n[1,2,3,4,5,6].x.chunk(2)\n// [ [1,2], [3,4], [5,6] ]\n\n[1,2,3,4,5].x.chunk(2)\n// [ [1,2], [3,4], [5] ]\n\n[1,2,3,4,5].x.chunk(2, true)\n// [ [1,2], [3,4] ]\n\n[1,2,3,4].x.chunk(0)\n// []\n```\n\n## `chunkByCallback(cb, matchedItemOpening = true)`\n\n```\n// open new chunk when meet even number\n[1,2,3,4,5,6].x.chunkByCallback(x =\u003e x % 2 === 0)\n// [ [1], [2,3], [4,5], [6] ]\n\n// close current chunk when meet even number\n[1,2,3,4,5,6].x.chunkByCallback(x =\u003e x % 2 === 0, false)\n// [ [1,2], [3,4], [5,6] ]\n\n// same as chunk(3)\n[1,2,3,4,5,6,7,8].x.chunkByCallback(\n  (_, idx) =\u003e (idx + 1) % 3 === 0,\n  false\n)\n// [ [1,2,3], [4,5,6], [7,8] ]\n```\n\n## `chunkByReduce(reducer, init = null, tester = null)`\n\n```\n[1,2,3,4,5,6,7,8].x.chunkByReduce(\n  (acc, curr) =\u003e acc + curr,\n  null,\n  sum =\u003e sum \u003e= 8\n)\n\n// [ [1,2,3,4],[5,6],[7,8] ]\n```\n\n```\n[1,2,3,4,5,6,7,8].x.chunkByReduce(\n  (acc, curr) =\u003e [...acc, curr],\n  [],\n  arr =\u003e arr.length === 2\n)\n\n// [ [1,2],[3,4],[5,6],[7,8] ]\n```\n\n## `chunkByPattern(...sizes, rejectStickingTail = false)`\n\n```\n[1,2,3,4,5,6].x.chunkByPattern(2, 3)\n// [ [1,2], [3,4,5], [6] ]\n\n[1,2,3,4,5,6].x.chunkByPattern(2, 3, true)\n// [ [1,2], [3,4,5] ]\n```\n\n## `chunkByGroup(grouper = item =\u003e 0)`\n\n```\n['foo', 'bar', 'baz', 'quux'].x.chunkByGroup(\n  str =\u003e str.length\n)\n\n// [ ['foo','bar','baz'], ['quux'] ]\n```\n\n* groups are sorted in ascending order, yet chunks themselves are not sorted:\n\n```\n[\n  'Alabama', 'Texas', 'Illinois',\n  'Arizona', 'Idaho'\n].x.chunkByGroup(str =\u003e str[0])\n\n// [ ['Alabama','Arizona'], ['Illinois','Idaho'], ['Texas'] ]\n```\n\n* if the callback returns a number, the smaller it is, the closer to the array's beginning the chunk will be:\n\n```\n[1,30,500,600,40,2].x.chunkByGroup(x =\u003e {\n  if (x \u003c 10) return 1;\n  if (x \u003c 100) return 2;\n  return 3;\n});\n\n// [ [1,2], [30,40], [500,600] ]\n```\n\n* grouping callback can be binary one, i.e., returning either `true` or `false`:\n\n```\n[1,2,3,4,5,6].x.chunkByGroup(x =\u003e x % 2 === 0)\n// [ [1,3,5], [2,4,6] ]\n```\n\nIn 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 `!`:\n\n```\n[1,2,3,4,5,6].x.chunkByGroup(x =\u003e !(x % 2 === 0))\n// [ [2,4,6], [1,3,5] ]\n```\n\n## `partition(partitioner = item =\u003e 0)`\n\nThe _partitioner_ should return a number. If you want to handle strings, use [`chunkByGroup()`](#chunkByGroupgrouper--item--0).  \n  \nThe smaller _partitioner_ result is, the closer to the array's beginning the chunk will be:\n\n```\n[1,30,500,600,40,2].x.partition(x =\u003e {\n  if (x \u003c 10) return 1;\n  if (x \u003c 100) return 2;\n  return 3;\n});\n\n// [ [1,2], [30,40], [500,600] ]\n```\n\n```\n[1,2,3,4,5,6].x.partition(x =\u003e x % 2 === 0)\n// [ [1,3,5], [2,4,6] ]\n\n// 1 % 2 === 0 -\u003e false, false -\u003e 0\n// 2 % 2 === 0 -\u003e true, true -\u003e 1\n\n\n[1,2,3,4,5,6].x.partition(x =\u003e !(x % 2 === 0))\n// [ [2,4,6], [1,3,5] ]\n\n// 1 % 2 === 0 -\u003e false, !false -\u003e true -\u003e 1\n// 2 % 2 === 0 -\u003e true, !true -\u003e false -\u003e 0\n```\n\n## `chunkReduce(size, reducer = null, init = null, rejectStickingTail = false)`\n\n```\n[1,2,3,4,5,6].x.chunkReduce(\n  2, (acc, curr) =\u003e acc + curr\n)\n// [3,7,11]\n\n[1,2,3,4,5,6].x.chunkReduce(\n  3, (acc, curr) =\u003e acc + curr\n)\n// [6,15]\n```\n\n```\n[1,2,3,4,5].x.chunkReduce(\n  2, (acc, curr) =\u003e acc + curr\n)\n// [3,7,5]\n\n[1,2,3,4,5].x.chunkReduce(\n  2, (acc, curr) =\u003e acc + curr,\n  null, true\n)\n// [3,7]\n```\n\n* double first member of every pair:\n\n```\n[1,2,3,4,5,6].x.chunkReduce(\n  2, (acc, curr) =\u003e acc + curr,\n  chunk =\u003e chunk[0]\n)\n// [4,10,16]\n\n// [1,2] -\u003e [1,1,2] =\u003e 1 + 1 + 2 = 4\n// [3,4] -\u003e [3,3,4] =\u003e 3 + 3 + 4 = 10\n// [5,6] -\u003e [5,5,6] =\u003e 5 + 5 + 6 = 16\n```\n\n## `frameReduce(size, reducer = null, init = null, rejectStickingTail = false)`\n\n```\n[1,2,3,4].x.frameReduce(\n  2, (acc, curr) =\u003e acc + curr\n)\n// [3,5,7,4]\n// [1+2, 2+3, 3+4, 4]\n\n[1,2,3,4].x.frameReduce(\n  2, (acc, curr) =\u003e acc + curr,\n  null, true\n)\n// [3,5,7]\n// [1+2, 2+3, 3+4]\n```\n\n```\n[1,2,3,4,5,6].x.frameReduce(\n  3, (acc, curr) =\u003e acc + curr,\n  null, true\n)\n// [6,9,12,15]\n// [1+2+3, 2+3+4, 3+4+5, 4+5+6]\n```\n\n## `zip(otherArrays...)`\n\n```\n['a', 'b', 'c', 'd'].x.zip([1, 2, 3, 4])\n// [ ['a', 1], ['b', 2], ['c', 3], ['d', 4] ]\n\n['a', 'b'].x.zip([1, 2], ['foo', 'bar'])\n// [ ['a', 1, 'foo'], ['b', 2, 'bar'] ]\n\n['a', 'b', 'c', 'd'].x.zip([1, 2])\n// [ ['a', 1], ['b', 2] ]\n\n['a', 'b'].x.zip([1, 2, 3, 4])\n// [ ['a', 1], ['b', 2] ]\n```\n\n## `zipAll(otherArrays...)`\n\n```\n['a', 'b'].x.zipAll([1, 2])\n// [ ['a', 1], ['b', 2] ]\n\n['a', 'b'].x.zipAll([1, 2], ['foo', 'bar'])\n// [ ['a', 1, 'foo'], ['b', 2, 'bar'] ]\n\n['a', 'b', 'c', 'd'].x.zipAll([1, 2])\n// [ ['a', 1], ['b', 2], ['c', undefined], ['d', undefined] ]\n\n['a', 'b'].x.zipAll([1, 2, 3, 4])\n// [ ['a', 1], ['b', 2], [undefined, 3], [undefined, 4] ]\n```\n\n### `zip()` vs. `zipAll()`\n\n```\n[1, 2, 3, 4].x.zip(['a', 'b'])\n// [ [1, 'a'], [2, 'b'] ]\n\n[1, 2, 3, 4].x.zipAll(['a', 'b'])\n// [ [1, 'a'], [2, 'b'], [3, undefined], [4, undefined] ]\n```\n\n## `unzip()`\n\n```\n[ ['a', 1], ['b', 2], ['c', 3], ['d', 4] ].x.unzip()\n// [ ['a', 'b', 'c', 'd'], [1, 2, 3, 4] ]\n\n[ ['a', 1, 'foo'], ['b', 2, 'bar'], ['c', 3, 'baz'] ].x.unzip()\n// [ ['a', 'b', 'c'], [1, 2, 3], ['foo', 'bar', 'baz'] ]\n\n[ ['a', 1], ['b', 2, 'foo'], ['c', 3, 'bar']].x.unzip()\n// [ ['a', 'b'], [1, 2] ]\n```\n\n## `unzipAll()`\n\n```\n[ ['a', 1, 'foo'], ['b', 2, 'bar'], ['c', 3, 'baz'] ].x.unzipAll()\n// [ ['a', 'b', 'c'], [1, 2, 3], ['foo', 'bar', 'baz'] ]\n\n[ ['a', 1], ['b', 2, 'foo'], ['c', 3, 'bar']].x.unzipAll()\n// [ ['a', 'b', 'c'], [1, 2, 3], [undefined, 'foo', 'bar'] ]\n```\n\n### `unzip()` vs. `unzipAll()`\n\n```\n[ ['a', 1], ['b', 2, 'foo'] ].x.unzip()\n// [ ['a', 'b'], [1, 2] ]\n\n[ ['a', 1], ['b', 2, 'foo'] ].x.unzipAll()\n// [ ['a', 'b'], [1, 2], [undefined, 'foo'] ]\n```\n\n## `split(matcher = null)`\n\n```\n[1,2,3,2,4,5].x.split(2)\n// [ [1], [3], [4,5] ]\n\n[1,2,3,4,5,6].x.split([2,4,8])\n// [ [1], [3], [5,6] ]\n\n[0,1,2,4,3,5,6,8,7,9].x.split(x =\u003e x % 2 === 0)\n// [ [1], [3,5], [7,9] ]\n\n[1,2,3,4].x.split(8)\n// [1,2,3,4]\n```\n\n## `splitByPattern(take, skip, from = null, rejectStickingTail = false)`\n\n```\n[1,2,3,4,5,6,7,8].x.splitByPattern(3, 2)\n// [ [1,2,3], [6,7,8] ]\n\n[1,2,3,4,5,6,7,8].x.splitByPattern(3, 2, 1)\n// [ [2,3,4], [7,8] ]\n\n[1,2,3,4,5,6,7,8].x.splitByPattern(3, 2, 1, true)\n// [ [2,3,4] ]\n```\n\n## `cut([index])`\n\n```\n[1,2,3,4].x.cut()\n// [ [1, 2], [3, 4] ]\n\n[1,2,3,4,5].x.cut()\n// [ [1,2], [3,4,5] ]\n\n[1,2,3,4,5,6].x.cut(2)\n// [ [1,2], [3,4,5,6] ]\n\n[1,2,3,4,5,6].x.cut(-2)\n// [ [1,2,3,4], [5,6] ]\n```\n\n## `chop(...cuts)`\n\n```\n[1,2,3,4,5,6].x.chop(2)\n// [ [1,2], [3,4,5,6] ]\n\n[1,2,3,4,5,6].x.chop(-2)\n// [ [1,2,3,4], [5,6] ]\n\n[1,2,3,4,5,6].x.chop(-2, 2)\n// [ [1,2], [3,4], [5,6] ]\n\n[1,2,3,4,5,6,7,8].x.chop(-2, 2)\n// [ [1,2], [3,4,5,6], [7,8] ]\n```\n\n```\n[1,2,3,4,5,6,7,8,9,0].chop(\n  1, 3, 6, 12\n)\n// [ [1], [2,3], [4,5,6], [7,8,9,0] ]\n```\n\n## `wrapAlter(processor)`\n\n```\nconst arr = [1,2,3,4];\nconst alt = arr.x.wrapAlter(a =\u003e {\n  a.pop();\n  a.push(5);\n});\n\nalt\n// [1,2,3,5]\narr\n// [1,2,3,4]\n```\n\n```\nconst arr = [1,2,3,4];\nconst alt = arr.x.wrapAlter(a =\u003e {\n  a.pop();\n  a.push(5);\n  return a.slice(2);\n});\n\nalt\n// [3,5]\narr\n// [1,2,3,4]\n```\n\n## `adapt(otherArray = [], adaptations = 1)`\n\n* adapt only one different value from other array:\n\n```\n[1,2,1,2].x.adapt([1,20,1,20])\n// [1,20,1,2]\n```\n\n* adapt two different values from other array:\n\n```\n[1,2,1,2,1,2].x.adapt([1,20,1,20,1,20], 2)\n// [1,20,1,20,1,2]\n```\n\n* adapt all different values from other array (use `-1` as a second parameter):\n\n```\n[1,2,1,2,1,2].x.adapt([1,20,1,20,1,20], -1)\n// [1,20,1,20,1,20]\n```\n\n## `adaptByCallback(otherArray = [], tester = null, adaptations = 1, lazy = false)`\n\n\u003e default tester: `(a, b) =\u003e a !== b`\n\n```\n[1,2,3,4,5,6].x.adaptByCallback(\n  [1,2,30,40,50,60],\n  (a, b) =\u003e (a % 2 === 0) \u0026\u0026 (b % 2 === 0)\n)\n\n// [1,2,3,40,5,6]\n// adapt first other value if both target and source value are even\n```\n\n* in _lazy mode_, every other value is counted whether or not has been adapted:\n\n```\n// normal mode\n[1,2,3,4,5,6].x.adaptByCallback(\n  [1,2,30,40,50,60],\n  x =\u003e x % 2 === 0,\n  2\n)\n// [1,2,3,40,5,60]\n\n// lazy mode\n[1,2,3,4,5,6].x.adaptByCallback(\n  [1,2,30,40,50,60],\n  x =\u003e x % 2 === 0,\n  2,\n  true\n)\n// [1,2,3,40,5,6]\n```\n\n## `random(count = 1)`\n\n```\n[1,2,3,4,5,6].x.random()\n// 4 (for example)\n\n[1,2,3,4,5,6].x.random(1)\n// 4 (for example)\n\n[1,2,3,4,5,6].x.random(2)\n// [5,2] (for example)\n\n[1,2,3,4,5,6].x.random(4)\n// [3,6,1,5] (for example)\n```\n\n## `shuffle()`\n\n```\n[1,2,3,4].x.shuffle()\n// [4,2,3,1] (for example)\n```\n\n## `move(sourceIndex, count, targetIndex = 0)`\n\n```\n// move first 3 items to index 2\n[1,2,3,4,5,6].x.move(0, 3, 2)\n// [4,5,1,2,3,6]\n\n// move last 2 items to index 0\n[1,2,3,4,5,6].x.move(-2, 2)\n// [5,6,1,2,3,4]\n\n// move first item to the end\n[1,2,3,4,5,6].x.move(0, 1, -1)\n// [2,3,4,5,6,1]\n\n// move last item to the beginning\n[1,2,3,4,5,6].x.move(-1, 1)\n// [6,1,2,3,4,5]\n\n// move second to last item to the end (swap two last items)\n[1,2,3,4,5,6].x.move(-2, 1, -1)\n// [1,2,3,4,6,5]\n```\n\n## `insert(index, ...items)`\n\n```\n[1,2,3,4].x.insert(0, 10, 20)\n// [10,20,1,2,3,4]\n\n[1,2,3,4].x.insert(2, 10, 20)\n// [1,2,10,20,3,4]\n\n[1,2,3,4].x.insert(-1, 10, 20)\n// [1,2,3,10,20,4]\n```\n\n## `overwrite(index, ...items)`\n\n```\n[1,2,3,4,5,6].x.overwrite(0, 7, 8)\n// [7,8,3,4,5,6]\n\n[1,2,3,4,5,6].x.overwrite(2, 7, 8)\n// [1,2,7,8,5,6]\n\n[1,2,3,4,5,6].x.overwrite(-1, 7, 8)\n// [1,2,3,4,5,7,8]\n```\n\n## `override(index, ...items)`\n\n```\n[1,2,3,4,5,6].x.override(0, 7, 8)\n// [7,8,3,4,5,6]\n\n[1,2,3,4,5,6].x.override(2, 7, 8)\n// [1,2,7,8,5,6]\n\n[1,2,3,4,5,6].x.override(-1, 7, 8)\n// [1,2,3,4,5,7]\n```\n\n### `overwrite()` vs. `override()`\n\n```\n[1,2,3,4].x.overwrite(2, 5, 6, 7, 8, 9)\n// [1,2,5,6,7,8,9]\n\n[1,2,3,4].x.override(2, 5, 6, 7, 8, 9)\n// [1,2,5,6]\n```\n\n## `cork(value)`\n\n```\n[1,2,3,4].x.cork(0)\n// [1,0,2,0,3,0,4]\n```\n\n```\n[1,2,3,4].x.cork([10,20])\n// [1,10,2,20,3,10,4]\n```\n\n```\n[1,2,3,4].x.cork(\n  (curr, next, idx) =\u003e (curr + next) / 2\n)\n// [1, 1.5, 2, 2.5, 3, 3.5, 4]\n```\n\n## `precede(appendix = [], fillWithUndefined = false)`\n\n```\n[1,2,3,4].x.precede(0)\n// [0,1,0,2,0,3,0,4]\n\n[1,2,3,4].x.precede([10,20,30,40])\n// [10,1,20,2,30,3,40,4]\n\n[1,2,3,4].x.precede(x =\u003e x * 2)\n// [2,1,4,2,6,3,8,4]\n```\n\n```\n[1,2].x.precede([10,20,30,40])\n// [10,1,20,2]\n\n[1,2,3,4].x.precede([10,20])\n// [10,1,20,2,3,4]\n\n[1,2,3,4].x.precede([10,20], true)\n// [10,1,20,2,undefined,3,undefined,4]\n```\n\n## `follow(appendix = [], fillWithUndefined = false)`\n\n```\n[1,2,3,4].x.follow(0)\n// [1,0,2,0,3,0,4,0]\n\n[1,2,3,4].x.follow([10,20,30,40])\n// [1,10,2,20,3,30,4,40]\n\n[1,2,3,4].x.follow(x =\u003e x * 2)\n// [1,2,2,4,3,6,4,8]\n```\n\n```\n[1,2].x.follow([10,20,30,40])\n// [1,10,2,20]\n\n[1,2,3,4].x.follow([10,20])\n// [1,10,2,20,3,4]\n\n[1,2,3,4].x.follow([10,20], true)\n// [1,10,2,20,3,undefined,4,undefined]\n```\n\n## `frozen()`\n\nDisable addition, removal and update.\n\n\u003e alias: `readonly()` or `readOnly()`\n\n```\nconst arr = [10,20];\narr.x.frozen();\n\narr.push(30)\n// TypeError: Cannot add property 2, object is not extensible\n\narr.pop()\n// TypeError: Cannot delete property '1' of [object Array]\n\narr[0] = 100\n\narr\n// [10,20]\n```\n\n## `fixed()`\n\nDisable addition and removal.\n\n```\nconst arr = [10,20];\narr.x.fixed();\n\narr.push(30)\n// TypeError: Cannot add property 2, object is not extensible\n\narr.pop()\n// TypeError: Cannot delete property '1' of [object Array]\n\narr[0] = 100\n\narr\n// [100,20]\n```\n\n## `dwarf()`\n\nDisable addition.\n\n```\nconst arr = [10,20];\narr.x.dwarf();\n\narr.push(30)\n// TypeError: Cannot add property 2, object is not extensible\n\narr.pop()\n\narr[0] = 100\n\narr\n// [100]\n```\n\n## `alterable()`\n\nReturn array with enabled addition, removal and update.\n\n```\nconst frozen = [1,2,3,4].x.frozen();\nconst fixed = [1,2,3,4].x.fixed();\nconst dwarf = [1,2,3,4].x.dwarf();\n\nconst unfrozen = frozen.x.alterable();\nconst unfixed = fixed.x.alterable();\nconst undwarf = dwarf.x.alterable();\n\nunfrozen.push(5);\nunfixed.push(5);\nundwarf.push(5);\n\nunfrozen // [1,2,3,4,5]\nunfixed // [1,2,3,4,5]\nundwarf // [1,2,3,4,5]\n```\n\n## `clamped(min, max)`\n\n```\nconst rgb = [].x.clamped(0, 255);\n\nrgb.push(-120);\nrgb.push(250);\nrgb.push(340);\n\nrgb\n// [0, 250, 255]\n```\n\n```\nconst digits = [-20,-4,1,8,12,28].x.clamped(-9, 9);\n// [-9,-4,1,8,9,9]\n\ndigits.push(-120);\ndigits.push(4);\ndigits.push(120);\n\ndigits\n// [-9,-4,1,8,9,9,-9,4,9]\n```\n\n## `fold(coverSelector)`/`unfold(index)`\n\n```\nconst users = [\n  { name: 'John', city: 'LA' },\n  { name: 'Mark', city: 'NY' }\n].x.fold(u =\u003e u.name);\n\nconsole.log(users[0]);\n// John\nconsole.log(users[1]);\n// Mark\n\nconsole.log(users.x.unfold(0));\n// { name: 'John', city: 'LA' }\nconsole.log(users.x.unfold(1));\n// { name: 'Mark', city: 'NY' }\n```\n\n```\nconst users = [\n  { name: 'John', city: 'LA' }\n].x.fold(u =\u003e u.name);\n\nusers.push({ name: 'Mark', city: 'NY' });\n\nconsole.log([...users]);\n// John, Mark\n\nconsole.log(\n  users.x.unfold(0),\n  users.x.unfold(1)\n);\n// { name: 'John', city: 'LA' }\n// { name: 'Mark', city: 'NY' }\n```\n\n## `sparse(slots = 1)`\n\n```\n[1, 2, 3, 4].x.sparse()\n// [1, empty, 2, empty, 3, empty, 4]\n\n[1, 2, 3, 4].x.sparse(1)\n// [1, empty, 2, empty, 3, empty, 4]\n\n[1, 2, 3, 4].x.sparse(2)\n// [1, empty, empty, 2, empty, empty, 3, empty, empty, 4]\n```\n\n## `thick()`\n\n```\n[1,2,,,,3,,,4].x.thick()\n// [1,2,3,4]\n```\n\n## `union(otherSet = [])`\n\n```\n[1,2,3,4].x.union([5,6,7,8])\n// [1,2,3,4,5,6,7,8]\n\n[1,2,3,4].x.union([3,4,5,6])\n// [1,2,3,4,5,6]\n```\n\n## `intersection(otherSet = [])`\n\n```\n[1,2,3,4].x.intersection([3,4,5,6])\n// [3,4]\n```\n\n## `diff(otherSet = [])`\n\n```\n[1,2,3,4].x.diff([3,4,5,6])\n// [1,2]\n```\n\n## `symDiff(otherSet = [])`\n\n```\n[1,2,3,4].x.symDiff([3,4,5,6])\n// [1,2,5,6]\n```\n\n## `complement(otherSet = [])`\n\n```\n[1,2,3,4].x.complement([3,4,5,6])\n// [5,6]\n```\n\n## `reduce(reducer, finalizer = null, init)`\n\n```\n[1,2,3,4].x.reduce(\n  (acc, curr) =\u003e acc + curr,\n  (sum, arr) =\u003e sum / arr.length\n)\n// 2.5 (average)\n```\n\n```\n[1,4,2,3].x.reduce(\n  ([currMax, itsIdx], curr, idx) =\u003e {\n    return idx === 0 || curr \u003e currMax ? [curr,idx] : [currMax,itsIdx];\n  },\n  ([currMax, itsIdx]) =\u003e itsIdx,\n  []\n)\n// 1 (index of largest number)\n```\n\n## `scan(reducer, init = null)`\n\n$scanArr_i = reduce(inputArr_{\u003c0;i\u003e}\\color{#888}{, init}\\color{#000})$\n\n```\n[1,2,3,4].x.scan((a, c) =\u003e a + c)\n// [1,3,6,10]\n\n// 1 -\u003e 1\n// 3 -\u003e 1 + 2\n// 6 -\u003e 1 + 2 + 3 or 3 + 3\n// 10 -\u003e 1 + 2 + 3 + 4 or 6 + 4\n```\n\n```\n['f', 'o', 'o'].x.scan((a, c) =\u003e a + c)\n// ['f', 'fo', 'foo']\n\n['b', 'a', 'r'].x.scan((a, c) =\u003e a + c, 'foo')\n// ['foob', 'fooba', 'foobar']\n```\n\n## `sample(size, step = 1)`\n\n```\n[1,2,3,4,5,6].x.sample(2)\n// [ [1,2],[2,3],[3,4],[4,5],[5,6] ]\n\n[1,2,3,4,5,6].x.sample(2, 2)\n// [ [1,2],[3,4],[5,6] ]\n\n[1,2,3,4,5,6].x.sample(2, 3)\n// [ [1,2],[4,5] ]\n```\n\n## `flat(level = -1)`\n\n* by default, `flat()` without parameter (or with default parameter `-1`) performs deep flat:\n\n```\n[1,2,[3,4,[5,6],7,8],9,10].x.flat()\n// [1,2,3,4,5,6,7,8,9,10]\n```\n\n* with parameter given, depth of flattening can be limited:\n\n```\n[1,[2,[3,[4,[5,[6,7],8],9],10],11],12].x.flat(3)\n// [1,2,3,4,[5,[6,7],8],9,10,11,12]\n```\n\n## `sort(...comparators)`\n\nevery `comparator` can be:\n- one-sign string:\n  * `-` to sort primitive values in descending order\n  * `+` to sort primitive values in ascending order\n- string that contains key to sort by\n  * `foo` to sort by `foo` key in ascending order\n  * `+foo` to sort by `foo` key in ascending order\n  * `-foo` to sort by `foo` key in descending order\n- callback - compare function like one passed to native `sort()` method\n\n```\n[2,3,1,4].x.sort()\n// [1,2,3,4]\n```\n\n```\nconst arr = [\n  { name: 'Mark', age: 20 },\n  { name: 'John', age: 20 },\n  { name: 'Adam', age: 22 }\n];\n\narr.x.sort(\n  '-age', 'name'\n);\n\narr.map(u =\u003e u.name)\n// ['Adam', 'John', 'Mark']\n```\n\n```\n['C', 'A', 'D', 'B'].x.sort('-')\n// ['D', 'C', 'B', 'A']\n```\n\n## `reversed()`\n\nReturn reversed array keeping original array unchanged.\n\n```\nconst arr = [1,2,3,4];\nconst rev = arr.x.reversed();\n\nrev\n// [4,3,2,1]\n\narr\n// [1,2,3,4]\n// original array unchanged\n```\n\n## `forEach(cb)`\n\nLike `forEach()` but with `break`/`continue` feature.\n\n\u003e alias: `each()`\n\n```\n[1,2,3,4].x.forEach((item, idx, api, array) =\u003e {\n  // api - break and continue\n\n  if (x === 4) return api.break;\n  console.log(item);\n});\n\n// 1 2 3\n```\n\n```\n[1,2,3,4].x.forEach((item, idx, api) =\u003e {\n  if (x % 2 !== 0) return api.continue;\n  console.log(item);\n});\n\n// 2 4\n```\n\n```\n[1,2,3,4,5,6].x.forEach((\n  item,\n  idx,\n  flow\n) =\u003e {\n  if (item % 2 !== 0) return flow.continue;\n  console.log(item);\n  if (item === 4) return flow.break;\n});\n\n// 2 4\n```\n\n## `spreadMap(mapper)`\n\n```\n[1,2,3,4].x.spreadMap(x =\u003e [x, x * x])\n// [1,1,2,4,3,9,4,16]\n\n[1,2,3,4].x.spreadMap(x =\u003e x * x)\n// [1,4,9,16]\n```\n\n```\n[1,2,3,4].x.spreadMap(x =\u003e [\n  x,\n  x % 2 === 0\n])\n// [1, false, 2, true, 3, false, 4, true]\n```\n\n## `filterMapped(mapper = null, filter = null)`\n\nFilters items that would pass a test if they were mapped.\n\n```\n[1,2,3,4,5,6,7,8,9].x.filterMapped(\n  x =\u003e Math.sqrt(x),\n  x =\u003e x === parseInt(x)\n)\n\n// [1,4,9]\n// numbers whose square root is integer\n```\n\n```\n['cat', 'dog', 'lion', 'tiger'].x.filterMapped(\n  str =\u003e str.length,\n  len =\u003e len \u003e 3\n)\n\n// ['lion', 'tiger']\n```\n\n## `replace(matcher = x =\u003e x, replacer = x =\u003e x)`\n\n```\n[1,2,1,2].x.replace(\n  2,\n  10\n)\n// [1,10,1,10]\n\n[1,2,3,4].x.replace(\n  [1,2,5,6],\n  10\n)\n// [10,10,3,4]\n\n[1,2,3,4].x.replace(\n  x =\u003e x % 2 === 0,\n  10\n)\n// [1,10,3,10]\n```\n\n```\nlet item = 2;\n[1,2,1,2].x.replace(\n  item,\n  x =\u003e x * 10\n)\n// [1,20,1,20]\n\n[1,2,3,4].x.replace(\n  [3,4,5,6],\n  x =\u003e x * 10\n)\n// [1,2,30,40]\n\n[1,2,3,4].x.replace(\n  x =\u003e x % 2 === 0,\n  x =\u003e x * 10\n)\n// [1,20,3,40]\n```\n\n## `findIndexes(matcher = null)`\n\n```\n[1,2,3,4,5,6].x.findIndexes(x =\u003e x % 2 === 0)\n// [1,3,5]\n\n[1,2,3,4,5,6].x.findIndexes([2,4])\n// [1,3]\n\n[1,2,3,1,2,3].x.findIndexes(3)\n// [2,5]\n\n[1,2,3,1,2,3].x.findIndexes()\n// []\n```\n\n## `includes(matcher = null)`\n\n```\n[1,2,3,4].x.includes(4)\n// true\n\n[1,2,3,4].x.includes([4,8])\n// true\n\n[1,2,3,4].x.includes(x =\u003e x % 2 === 0)\n// true\n\n[1,2,3,4].x.includes(x =\u003e x \u003e= 100)\n// false\n\n[1,2,3,4].x.includes()\n// false\n```\n\n## `mapReduce(chunkSize = 2, reducer = null, init = null, rejectStickingTail = false)`\n\n\u003e default reducer: `(a, c) =\u003e a + c`\n\n```\n[10,20,30,40].x.mapReduce(\n  2, (acc, curr) =\u003e acc + curr\n)\n// [30,70]\n```\n\n```\n['\u003c', 'div', '\u003e', '\u003c', 'span', '\u003e'].x.mapReduce(\n  3, (acc, curr) =\u003e acc + curr\n)\n// ['\u003cdiv\u003e', '\u003cspan\u003e']\n```\n\n* `init` parameter can be a literal value or a callback:\n\n```\n[1,2,3,4,5,6].x.mapReduce(\n  2,\n  (acc, curr) =\u003e acc + curr,\n  (chunk, chunkNumber) =\u003e chunkNumber\n)\n// [4,9,14]\n\n// [1+1+2, 2+3+4, 3+5+6]\n// [1+3, 2+7, 3+11]\n```\n\n```\n// average of every triplets\n// sticking tail rejected\n\n[1,2,3,4,5,6,7,8].x.mapReduce(\n  3,\n  (acc, curr, idx, chunk) =\u003e {\n    let res = acc + curr;\n    if (idx === chunk.length - 1) res = res / chunk.length;\n    return res;\n  },\n  null,\n  true\n)\n// [2,5]\n\n// [(1+2+3)/3, (4+5+6)/3]\n```\n\n## `pop(n = 1)`\n\n```\nconst arr = [1,2,3,4,5,6];\n\narr.x.pop(2);\n// returns removed items: [5,6]\n\narr\n// [1,2,3,4]\n```\n\n## `shift(n = 1)`\n\n```\nconst arr = [1,2,3,4,5,6];\n\narr.x.shift(2);\n// returns removed items: [1,2]\n\narr\n// [3,4,5,6]\n```\n\n## `pull(index)`\n\n```\nconst arr = [1,2,3,4];\n\narr.x.pull(2)\n// 3\narr\n// [1,2,4]\n```\n\n```\nconst arr = [1,2,3,4];\n\narr.x.pull()\n// 4\narr\n// [1,2,3]\n```\n\n## `remove(valuesOrCb)`\n\n* single literal value:\n\n```\nconst arr = [1,2,3,2,1];\n\narr.x.remove(2)\n// [2,2]\n\narr\n// [1,3,1]\n```\n\n* array of values:\n\n```\nconst arr = [1,2,3,4,3,2,1];\n\narr.x.remove([1,2,8])\n// [1,2,2,1]\n\narr\n// [3,4,3]\n```\n\n* callback:\n\n```\nconst arr = [1,2,3,4];\n\narr.x.remove(x =\u003e x % 2 !== 0)\n// [1,3]\n\narr\n// [2,4]\n```\n\n## `fill(valueOrGenerator)`\n\n```\n[1,2,3,4].x.fill(0)\n// [0,0,0,0]\n```\n\n```\n[0,0,0,0].x.fill([1,2])\n// [1,2,1,2]\n```\n\n```\n(new Array(4)).x.fill(idx =\u003e idx + 1)\n// [1,2,3,4]\n```\n\n## `generate(length, valueGenerator)`\n\n```\n[].x.generate(4, 0)\n// [0,0,0,0]\n\n[].x.generate(4, idx =\u003e idx + 1)\n// [1,2,3,4]\n\n[1].x.generate(4, (idx, curr) =\u003e {\n  const lastItem = curr[curr.length - 1];\n  return lastItem * 10;\n})\n// [1,10,100,1000]\n```\n\n## `padLeft(maxLength, value)`\n\n\u003e alias: `pad()`\n\n```\n[1,2].x.padLeft(4, 0)\n// [0,0,1,2]\n\n[1,2].x.pad(4, 0)\n// [0,0,1,2]\n```\n\n## `padRight(maxLength, value)`\n\n```\n[1,2].x.padRight(4, 0)\n// [1,2,0,0]\n\n[1,2,3,4,5,6].x.padRight(4, 0)\n// [1,2,3,4,5,6]\n```\n\n## `align(length, filling, padSide = 'left', cutSide = 'right')`\n\n```\n[1,2,3,4].x.align(6, 0)\n// [0,0,1,2,3,4]\n\n[1,2,3,4].x.align(6, 0, 'right')\n// [1,2,3,4,0,0]\n\n[1,2,3,4].x.align(2)\n// [1,2]\n\n[1,2,3,4].x.align(2, null, null, 'left')\n// [3,4]\n```\n\n## `join(separator)`\n\n```\n[1,2,3,4].x.join('-')\n// '1-2-3-4'\n```\n\n```\n[4,8,1,0,0,8,0,8,0].x.join(\n  (item, idx, accStr) =\u003e {\n    if (idx === 1) return ' ';\n    if (idx === 4 || idx === 6) return '-';\n    return '';\n  }\n)\n// '48 100-80-80'\n```\n\n## `matchSome(...tests)`\n\n```\n[1,2,3,4,5,6].x.matchSome(\n  x =\u003e x % 2 === 0,\n  x =\u003e x \u003e= 3\n)\n// [2,3,4,5,6]\n```\n\n## `matchEvery(...tests)`\n\n```\n[1,2,3,4,5,6].x.matchEvery(\n  x =\u003e x % 2 === 0,\n  x =\u003e x \u003e= 3\n)\n// [4,6]\n```\n\n## `toString(itemMapper = null, separator = null, emptyMsg = null)`\n\n* define only mapper:\n\n```\nconst arr = [1,2,3,4];\narr.x.toString(x =\u003e x * 10);\n\narr + ''\n// '10,20,30,40'\n```\n\n* define only separator:\n\n```\nconst arr = [1,2,3,4];\narr.x.toString(null, ' - ');\n\narr + ''\n// '1 - 2 - 3 - 4'\n```\n\n* define only alternative text for empty array:\n\n```\nconst arr = [];\narr.x.toString(null, null, 'no items');\n\narr + ''\n// 'no items'\n\narr.push(1,2);\narr + ''\n// '1,2'\n```\n\n* define mapper, separator and alternative text:\n\n```\nconst arr = [\n  { id: 1, name: 'John', city: 'LA' },\n  { id: 2, name: 'Mark', city: 'NY' }\n];\n\narr.x.toString(\n  u =\u003e u.name + ', ' + u.city,\n  '; ',\n  'no users'\n);\n\narr + ''\n// 'John, LA; Mark, NY'\n\narr.splice(0, 2);\narr + ''\n// 'no users'\n```\n\n* remove custom stuff:\n\n```\nconst arr = [1,2,3,4];\narr.x.toString(x =\u003e x * 10);\n\narr + ''\n// '10,20,30,40'\n\narr.x.toString()\n\narr + ''\n// '1,2,3,4'\n```\n\n## `toJSON(mapper = null)`\n\n```\nconst arr = [1,2,3,4];\narr.x.toJSON(nums =\u003e {\n  const even = nums.filter(n =\u003e n % 2 === 0);\n  return even.map(n =\u003e n * 10);\n});\n\nJSON.stringify({\n  nums: arr\n});\n// '{\"nums\":[20,40]}'\n```\n\n```\nconst arr = [1,2,3,4];\narr.x.toJSON(nums =\u003e nums.reduce(\n  (acc, curr) =\u003e acc + curr\n));\n\nJSON.stringify({\n  numsSum: arr\n});\n// '{\"numsSum\":10}'\n```\n\n```\nconst arr = [1,2,3,4];\narr.x.toJSON(nums =\u003e nums.x.toString(\n  x =\u003e x * 10,\n  '/'\n));\n\nJSON.stringify({\n  str: arr\n});\n// '{\"str\":\"10/20/30/40\"}'\n```\n\n## `frame(size, rejectIncomplete = true)`\n\n```\n[1,2,3,4,5,6].x.frame(3)\n// [ [1,2,3],[2,3,4],[3,4,5],[4,5,6] ]\n\n[1,2,3,4,5,6].x.frame(3, false)\n// [ [1,2,3],[2,3,4],[3,4,5],[4,5,6], [5,6], [6] ]\n```\n\n## `audit(tester, frameSize = 2)`\n\n* check if items are sorted in ascending order:\n\n```\n[1,2,3,4].x.audit(\n  (l, r) =\u003e l \u003c= r\n)\n// true\n\n[1,4,2,3].x.audit(\n  (l, r) =\u003e l \u003c= r\n)\n// false\n```\n\n* check if item is sum of preceding two items:\n\n```\n[1,2,3,5,8].x.audit(\n  (l, r, sum) =\u003e l + r === sum,\n  3\n)\n// true\n```\n\n* check if item is a string that starts with preceding item:\n\n```\n['foo', 'foo bar', 'foo bar baz'].x.audit(\n  (leftString, rightString) =\u003e rightString.indexOf(leftString) === 0\n)\n// true\n```\n\n## `auditChunks(tester, chunkSize = 2, rejectSticking = true)`\n\n* check if every second word is reversed variant of preceding one:\n\n```\n['foo', 'oof', 'bar', 'rab'].x.auditChunks(\n  (word, reversed) =\u003e word === [...reversed].reverse().join('')\n)\n// true\n```\n\n* check if every three items fit shape `[A, B, A + B]`:\n\n```\n[1,2,3,4,5,9,14,18].x.auditChunk(\n  (l, r, sum) =\u003e l + r === sum,\n  3\n)\n// true\n\n// 1 + 2 = 3 -\u003e true\n// 4 + 5 = 9 -\u003e true\n// -\u003e true\n\n\n// 14 and 18 are ignored as last chunk is \"sticking\"\n// i.e., chunk length (2) is less than required chunk size (3)\n\n// to include sticking chunk, use false as third parameter:\n\n\n[1,2,3,4,5,9,14,18].x.auditChunk(\n  (l, r, sum) =\u003e l + r === sum,\n  3,\n  false\n)\n// false\n\n// 1 + 2 = 3 -\u003e true\n// 4 + 5 = 9 -\u003e true\n// 14 + 18 = undefined -\u003e false\n// -\u003e false\n```\n\n## `audit()` vs. `auditChunks()`\n\n| Call | Comparisons performed |\n|--|--|\n| `[1,2,3,4,5,6].x.audit(...)` | `1,2`, `2,3`, `3,4`, `4,5` and `5,6` |\n| `[1,2,3,4,5,6].x.auditChunks(...)` | `1,2`, `3,4` and `5,6` |\n\n* `audit()` takes all possible chunks:\n\n```\n[10,20,14,18].x.audit(\n  (l, r) =\u003e l \u003c= r\n)\n// false\n\n// 10 \u003c= 20 -\u003e true\n// 20 \u003c= 14 -\u003e false\n// -\u003e false\n```\n\n* `auditChunks()` takes only adjacent chunks:\n\n```\n[10,20,14,18].x.auditChunks(\n  (l, r) =\u003e l \u003c= r\n)\n// true\n\n// 10 \u003c= 20 -\u003e true\n// 14 \u003c= 18 -\u003e true\n// -\u003e true\n```\n\n## `expandTo(target, cb, prevs, init = 0, includeSticking = false, maxIters = 32)`\n\n\u003e To compute another value, `N` last items are taken.  \n\u003e `N` equals value of the `prevs` parameter if exists, otherwise it looks for arity of the `cb` function.\n\n```\n[1,1].x.expandTo(8, (a, b) =\u003e a + b)\n// [1,1,2,3,5,8]\n```\n\n```\n[1].x.expandTo(8, (a, b) =\u003e a + b, 2, 1)\n// [1,2,3,5,8]\n```\n\nIf `target` is a function, it should return a boolean indicating whether or not the expanding is going on:\n\n```\n[1].x.expandTo(\n  x =\u003e x \u003c= 8,\n  (a, b) =\u003e a + b,\n  2, 1\n)\n// [1,2,3,5,8]\n\n[1,2].x.expandTo(\n  x =\u003e x !== -4,\n  (a, b) =\u003e a - b,\n  2, 0, true\n)\n// [1,2,-1,3,-4]\n```\n\n## `expandToLength(length, cb, prevs, init = 0)`\n\n\u003e To compute another value, `N` last items are taken.  \n\u003e `N` equals value of the `prevs` parameter if exists, otherwise it looks for arity of the `cb` function.\n\n\n```\n[1,1].x.expandToLength(8, (a, b) =\u003e a + b)\n// [1,1,2,3,5,8,13,21]\n\n[1].x.expandToLength(8, (a, b) =\u003e a + b, 2, 0)\n// [1,1,2,3,5,8,13,21]\n\n[1].x.expandToLength(8, (a, b) =\u003e a + b, 2, 1)\n// [1,2,3,5,8,13,21,34]\n```\n\n```\n['a', 'b', 'c'].x.expandToLength(6, (a, b) =\u003e a + b)\n// ['a', 'b', 'c', 'bc', 'cbc', 'bccbc']\n\n['a', 'b', 'c'].x.expandToLength(6, (a, b, c) =\u003e a + b + c)\n// ['a', 'b', 'c', 'abc', 'bcabc', 'cabcbcabc']\n```\n\n```\n['a', 'b', 'c'].x.expandToLength(6, (a, b, c) =\u003e a + b + c, 3)\n// ['a', 'b', 'c', 'abc', 'bcabc', 'cabcbcabc']\n\n// a + b + c -\u003e abc =\u003e [a, b, c, abc]\n// b + c + abc -\u003e bcabc =\u003e [a, b, c, abc, bcabc]\n// c + abc + bcabc -\u003e cabcbcabc =\u003e [a, b, c, abc, bcabc, cabcbcabc]\n\n\n['a', 'b', 'c'].x.expandToLength(6, (a, b) =\u003e a + b, 3)\n// ['a', 'b', 'c', 'ab', 'bc', 'cab']\n\n// a + b [c ignored] -\u003e ab =\u003e [a, b, c, ab]\n// b + c [ab ignored] -\u003e bc =\u003e [a, b, c, ab, bc]\n// c + ab [bc ignored] -\u003e cab =\u003e [a, b, c, ab, bc, cab]\n```\n\n## `forEachChunk(chunkSize, cb)`\n\n```\n[1,2,3,4].x.forEachChunk(2, (chunk, chunkNumber, arr) =\u003e {\n  console.log(chunkNumber, chunk);\n});\n\n// 1, [1,2]\n// 2, [3,4]\n```\n\n```\n[1,2,3,4].x.forEachChunk(3, (chunk, chunkNumber, arr) =\u003e {\n  console.log(chunkNumber, chunk);\n});\n\n// 1, [1,2,3]\n// 2, [4]\n```\n\n## `forEachFrame(blockSize, cb, fullBlocksOnly = false)`\n\n```\n[1,2,3,4,5].x.forEachFrame(3, (block, idx) =\u003e {\n  console.log(block);\n});\n\n// [1,2,3]\n// [2,3,4]\n// [3,4,5]\n// [4,5]\n// [5]\n```\n\n```\n[1,2,3,4,5].x.forEachFrame(3, (block, idx) =\u003e {\n  console.log(block);\n}, true);\n\n// [1,2,3]\n// [2,3,4]\n// [3,4,5]\n```\n\n## `minPairDiff(comparer = null)`\n\n\u003e default comparer: `(l, r) =\u003e Math.abs(l - r)`\n\n```\n[20,10,40].x.minPairDiff()\n// 10\n\n// [ Math.abs(20 - 10), Math.abs(10 - 40) ]\n// [ 10, 30 ]\n// min -\u003e 10\n```\n\n* with custom comparer:\n\n```\n[20,10,40].x.minPairDiff(\n  (l, r) =\u003e l - r\n)\n// -30\n\n// [ 20 - 10, 10 - 40 ]\n// [ 10, -30 ]\n// min -\u003e -30\n```\n\n## `maxPairDiff(comparer = null)`\n\n\u003e default comparer: `(l, r) =\u003e Math.abs(l - r)`\n\n```\n[20,10,40].x.maxPairDiff()\n// 30\n\n// [ Math.abs(20 - 10), Math.abs(10 - 40) ]\n// [ 10, 30 ]\n// max -\u003e 30\n```\n\n* with custom comparer:\n\n```\n[20,10,40].x.maxPairDiff(\n  (l, r) =\u003e l - r\n)\n// 10\n\n// [ 20 - 10, 10 - 40 ]\n// [ 10, -30 ]\n// max -\u003e 10\n```\n\n## `minDiff(comparer = null)`\n\n\u003e default comparer: `(l, r) =\u003e Math.abs(l - r)`\n\n```\n[20,80,40,10].x.minDiff()\n// 10\n\n// [\n//   |20-80|, |20-40|, |20-10|,\n//   |80-20|, |80-40|, |80-10|,\n//   |40-20|, |40-80|, |40-10|,\n//   |10-20|, |10-80|, |10-40|\n// ]\n\n// [\n//   60, 20, 10,\n//   60, 40, 70,\n//   20, 40, 30,\n//   10, 70, 30\n// ]\n\n// min -\u003e 10\n```\n\n* with custom comparer:\n\n```\n[20,80,40,10].x.minDiff(\n  (l, r) =\u003e l / r\n)\n// 0.125\n\n// [\n//   20/80, 20/40, 20/10,\n//   80/20, 80/40, 80/10,\n//   40/20, 40/80, 40/10,\n//   10/20, 10/80, 10/40\n// ]\n\n// [\n//   0.25, 0.5, 2,\n//   4, 2, 8,\n//   2, 0.5, 4,\n//   0.5, 0.125, 0.25\n// ]\n\n// min -\u003e 0.125\n```\n\n## `maxDiff(comparer = null)`\n\n\u003e default comparer: `(l, r) =\u003e Math.abs(l - r)`\n\n```\n[20,80,40,10].x.maxDiff()\n// 70\n\n// [\n//   |20-80|, |20-40|, |20-10|,\n//   |80-20|, |80-40|, |80-10|,\n//   |40-20|, |40-80|, |40-10|,\n//   |10-20|, |10-80|, |10-40|\n// ]\n\n// [\n//   60, 20, 10,\n//   60, 40, 70,\n//   20, 40, 30,\n//   10, 70, 30\n// ]\n\n// max -\u003e 70\n```\n\n* with custom comparer:\n\n```\n[20,80,40,10].x.maxDiff(\n  (l, r) =\u003e l / r\n)\n// 8\n\n// [\n//   20/80, 20/40, 20/10,\n//   80/20, 80/40, 80/10,\n//   40/20, 40/80, 40/10,\n//   10/20, 10/80, 10/40\n// ]\n\n// [\n//   0.25, 0.5, 2,\n//   4, 2, 8,\n//   2, 0.5, 4,\n//   0.5, 0.125, 0.25\n// ]\n\n// max -\u003e 8\n```\n\n## `minDiffAhead(comparer = null)`\n\n\u003e default comparer: `(l, r) =\u003e Math.abs(l - r)`\n\n```\n[10 80 200 20].x.minDiffAhead()\n// 10\n\n// [\n//   |10-80|, |10-200|, |10-20|,\n//   |80-200|, |80-20|,\n//   |200-20|\n// ]\n// \n// [\n//  70, 190, 10,\n//  120, 60,\n//  180\n// ]\n// \n// min -\u003e 10\n```\n\n* with custom comparer:\n\n```\n[10 80 200 20].x.minDiffAhead(\n  (l, r) =\u003e l - r\n)\n// -190\n\n// [\n//   10-80, 10-200, 10-20,\n//   80-200, 80-20,\n//   200-20\n// ]\n// \n// [\n//  -70, -190, -10,\n//  -120, 60,\n//  180\n// ]\n// \n// min -\u003e -190\n```\n\n## `maxDiffAhead(comparer = null)`\n\n\u003e default comparer: `(l, r) =\u003e Math.abs(l - r)`\n\n```\n[10 80 200 20].x.maxDiffAhead()\n// 190\n\n// [\n//   |10-80|, |10-200|, |10-20|,\n//   |80-200|, |80-20|,\n//   |200-20|\n// ]\n// \n// [\n//  70, 190, 10,\n//  120, 60,\n//  180\n// ]\n// \n// max -\u003e 190\n```\n\n* with custom comparer:\n\n```\n[10 80 200 20].x.maxDiffAhead(\n  (l, r) =\u003e l - r\n)\n// 180\n\n// [\n//   10-80, 10-200, 10-20,\n//   80-200, 80-20,\n//   200-20\n// ]\n// \n// [\n//  -70, -190, -10,\n//  -120, 60,\n//  180\n// ]\n// \n// max -\u003e 180\n```\n\n## `maxPairDiff()` vs. `maxDiff()` vs. `maxDiffAhead()`\n\n* `maxPairDiff()` compares every sibling\n* `maxDiff()` compares every item with all other items\n* `maxDiffAhead()` compares every item with all other items that follow it\n\nFor `[1,2,3,4]`:\n\n| Method | Comparisons performed |\n|--|--|\n| `maxPairDiff()` | 1-2, 2-3 and 3-4 |\n| `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 |\n| `maxDiffAhead()` | 1-2, 1-3, 1-4, 2-3, 2-4 and 3-4 |","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdamianc%2Farray-x","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdamianc%2Farray-x","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdamianc%2Farray-x/lists"}