1
原文地址:JavaScript30秒, 从入门到放弃之Array(六)

博客地址:JavaScript30秒, 从入门到放弃之Array(六)

水平有限,欢迎批评指正

tail

Returns all elements in an array except for the first one.

Return Array.slice(1) if the array's length is more than 1, otherwise, return the whole array.

const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);

返回除了数组第一个元素以外的所有元素。

如果数组长度大于1,则用Array.slice(1)返回;否则返回整个数组。

➜  code cat tail.js
const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);

console.log(tail([1, 2, 3]));
console.log(tail([1]));
➜  code node tail.js
[ 2, 3 ]
[ 1 ]

take

Returns an array with n elements removed from the beginning.

Use Array.slice() to create a slice of the array with n elements taken from the beginning.

const take = (arr, n = 1) => arr.slice(0, n);

返回一个由数组的前n个元素组成的新数组。

Array.slice()创建一个新的数组,数组元素由指定数组的前n个元素组成。

➜  code cat take.js
const take = (arr, n = 1) => arr.slice(0, n);

console.log(take([1, 2, 3], 5));
console.log(take([1, 2, 3], 0));
➜  code node take.js
[ 1, 2, 3 ]
[]

n可以指定为0,即一个也不取出。省略则n = 1

takeRight

Returns an array with n elements removed from the end.

Use Array.slice() to create a slice of the array with n elements taken from the end.

const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);

返回一个由数组的后n个元素组成的新数组。

Array.slice()创建一个新的数组,数组元素由指定数组的后n个元素组成。

➜  code cat takeRight.js
const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);

console.log(takeRight([1, 2, 3], 2));
console.log(takeRight([1, 2, 3]));

➜  code node takeRight.js
[ 2, 3 ]
[ 3 ]

拿数组后n个元素只需从数组长度减去n的位置开始取到结尾就可以了,slice第二个参数是可以省略的,因为是取到最后一个元素。

takeRightWhile

Removes elements from the end of an array until the passed function returns true. Returns the removed elements.

Loop through the array, using a for...of loop over Array.keys() until the returned value from the function is true. Return the removed elements, using Array.reverse() and Array.slice().

const takeRightWhile = (arr, func) => {
  for (let i of arr.reverse().keys())
    if (func(arr[i])) return arr.reverse().slice(arr.length - i, arr.length);
  return arr;
};

从数组尾部开始删除元素,直到对数组元素运用指定方法fntrue为止。同时返回被删除的元素。

循环数组,使用for…of循环Array.keys()直到对数组元素调用指定方法返回true为止。最后返回删除的所有元素,过程中结合了Array.reverse()Array.slice()

➜  code cat takeRightWhile.js
const takeRightWhile = (arr, func) => {
  for (let i of arr.reverse().keys())
    if(func(arr[i]))
      return arr.reverse().slice(arr.length - i, arr.length);
    return arr;
};

console.log(takeRightWhile([1, 2, 3, 4], n => n < 3));

➜  code node takeRightWhile.js
[ 3, 4 ]
for (let i of arr.reverse().keys())

循环数组arrkey值,即索引,因为是reverse(),所以是反向循环。如一个数组有五个元素,那么i就是4->3->2->1->0这样的顺序。

if(func(arr[i]))
  return arr.reverse().slice(arr.length - i, arr.length);

对数组元素arr[i]调用func,若真,此时的i就是顺数的第一个该删除的元素的索引,要删除的元素就是从此直到数组结尾为止;即arr.reverse().slice(arr.length - i, arr.length)包含的索引元素。

return arr;

如果前面的整个遍历过程中func(arr[i])都不为true的话,那就返回原数组,合情合理。

takeWhile

Removes elements in an array until the passed function returns true. Returns the removed elements.

Loop through the array, using a for...of loop over Array.keys() until the returned value from the function is true. Return the removed elements, using Array.slice().

const takeWhile = (arr, func) => {
 for (let i of arr.keys()) if (func(arr[i])) return arr.slice(0, i);
 return arr;
};

从数组索引为0开始删除元素,直到对数组元素运用指定方法fntrue为止。同时返回被删除的元素。

➜  code cat takeWhile.js
const takeWhile = (arr, fn) => {
  for (let i of arr.keys()) if(fn(arr[i])) return arr.slice(0, i);
  return arr;
};

console.log(takeWhile([1, 2, 3, 4], n => n >= 3));

➜  code node takeWhile.js
[ 1, 2 ]

takeRightWhile正好相反,而且还更容易理解。没什么可说的了。

union

Returns every element that exists in any of the two arrays once.

Create a Set with all values of a and b and convert to an array.

const union = (a, b) => Array.from(new Set([...a, ...b]));

返回两个数组的并集(像集合的并集一样,不包含重复元素)。

创建一个以ab数组为元素的集合并把它转化成数组。

➜  code cat union.js
const union = (a, b) => Array.from(new Set([...a, ...b]));

console.log(union([1, 2, 3], [4, 3, 2]));

➜  code node union.js
[ 1, 2, 3, 4 ]

我自己写的如下:

const union = (a, b) => [...new Set([...a, ...b])];

直接用ES6扩展运算符也能达到效果。

原理太简单,创建ab数组的集合自然把他们两者的重复元素去掉了。

unionBy

Returns every element that exists in any of the two arrays once, after applying the provided function to each array element of both.

Create a Set by applying all fn to all values of a. Create a Set from a and all elements in b whose value, after applying fn does not match a value in the previously created set. Return the last set converted to an array.

const unionBy = (a, b, fn) => {
  const s = new Set(a.map(v => fn(v)));
  return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))]));
};

对两个数组的元素分别调用指定方法后,返回以运行结果为判定基准的并集,并集是原始数组元素的并集而不是运行结果的并集。

创建一个a数组调用fn后的集合a1。再创建一个以数组a和对数组b进行过滤所有存在于集合a1中的元素后所剩余元素组成的数组为基准的集合。并把该集合转换成最终的数组。

➜  code cat unionBy.js
const unionBy = (a, b, fn) => {
  const s = new Set(a.map(v => fn(v)));

  return Array.from(new Set([...a, ...b.filter(v => !s.has(fn(v)))]));
};

console.log(unionBy([2.1], [1.2, 2.3], Math.floor));

➜  code node unionBy.js
[ 2.1, 1.2 ]
const s = new Set(a.map(v => fn(v)));

首先得创建其中一个数组的集合s

b.filter(v => !s.has(fn(v)))

这里就是把b数组中所有存在于a调用fn后生成的集合s的元素都删除掉。这样剩下的所有元素和a数组再进行集合运算后再转换成数组。就是我们所需要的结果。

unionWith

Returns every element that exists in any of the two arrays once, using a provided comparator function.

Create a Set with all values of a and values in b for which the comparator finds no matches in a, using Array.findIndex().

const unionWith = (a, b, comp) =>
  Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)]));

对两个数组的元素分别调用指定比较方法后,返回以运行结果为判定基准的并集,并集是原始数组元素的并集而不是运行结果的并集。

➜  code cat unionWith.js
const unionWith = (a, b, comp) =>
  Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)]));

console.log(unionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)));

➜  code node unionWith.js
[ 1, 1.2, 1.5, 3, 0, 3.9 ]

分主客体,这里主体是前一个数组,即a,表示数组a的所有元素都会保留下来。然后循环数组b,用findIndex方法去把所有对ab的元素调用comp比较方法后的结果不存在于a数组中的所有元素筛选出来。最后把筛选出来的所有元素和数组a组成新数组后再进行集合运算并把运算结果转化为数组。那就是unionWith的最终结果。

uniqueElements

Returns all unique values of an array.

Use ES6 Set and the ...rest operator to discard all duplicated values.

const uniqueElements = arr => [...new Set(arr)];

数组去重。

➜  code cat uniqueElements.js
const uniqueElements = arr => [...new Set(arr)];

console.log(uniqueElements([1, 2, 2, 3, 4, 4, 5]));

➜  code node uniqueElements.js
[ 1, 2, 3, 4, 5 ]

结合ES6的扩展运算符和集合便很容易实现。

unzip

Creates an array of arrays, ungrouping the elements in an array produced by zip.

Use Math.max.apply() to get the longest subarray in the array, Array.map() to make each element an array. Use Array.reduce() and Array.forEach() to map grouped values to individual arrays.

const unzip = arr =>
  arr.reduce(
    (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
    Array.from({
      length: Math.max(...arr.map(x => x.length))
    }).map(x => [])
  );

对于给定的多个数组,返回一个新的二维数组,数组的第一个元素包含多个数组的第一个元素,数组的第二个元素包含多个数组的第二个元素,以此类推(即把zip方法分好组的数组逆向解组)。

使用Math.max.apply()方法来获取输入数组的子数组元素个数的最大长度,使用Array.map()来把每一个元素创建成一个数组。然后使用Array.reduce()Array.forEach()去把组里的元素分别加到各自的数组中。

➜  code cat unzip.js
const unzip = arr =>
  arr.reduce((acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
    Array.from({
      length: Math.max(...arr.map(x => x.length))
    }).map(x => [])
  );

console.log(unzip([['a', 1, true], ['b', 2, false]]));
console.log(unzip([['a', 1, true], ['b', 2]]));

➜  code node unzip.js
[ [ 'a', 'b' ], [ 1, 2 ], [ true, false ] ]
[ [ 'a', 'b' ], [ 1, 2 ], [ true ] ]
Array.from({
  length: Math.max(...arr.map(x => x.length))
}).map(x => [])

这就是reduce的初始二维数组,用Array.from来生成一个数组,然后再map(x => [])成一个二维数组,那么数组的长度怎么定呢?因为被unzip的原数组里的元素可能是长度不同的数组。那么肯定是以长度最长的那个为准,这样才能包含解组后的所有元素。这就是length: Math.max(...arr.map(x => x.length))做的事。

对于reduce里的方法:

(acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc)

acc是累加值,在遍历过程中会一直变化,val.forEach((v, i) => acc[i].push(v))这是遍历过程中val数组元素push到累加acc对应索引数组的方法。

举个例子:

原数组arr = [[1, 2, 3], ['a', 'b']],在遍历过程中初始累加acc = [[], [], []](含有三个元素的数组)。

// 第一次
val = [1, 2, 3]
acc = [[1], [2], [3]]

// 第二次
val = ['a', 'b']
acc = [[1, 'a'], [2, 'b'], [3]] // 这也是最终结果

unzipWith

Creates an array of elements, ungrouping the elements in an array produced by zip and applying the provided function.

Use Math.max.apply() to get the longest subarray in the array, Array.map() to make each element an array. Use Array.reduce() and Array.forEach() to map grouped values to individual arrays. Use Array.map() and the spread operator (...) to apply fn to each individual group of elements.

const unzipWith = (arr, fn) =>
  arr
    .reduce(
      (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
      Array.from({
        length: Math.max(...arr.map(x => x.length))
      }).map(x => [])
    )
    .map(val => fn(...val));

对于给定的多个数组,返回一个新的二维数组,数组的第一个元素包含多个数组的第一个元素,数组的第二个元素包含多个数组的第二个元素,以此类推(即把zip方法分好组的数组逆向解组),在此基础上对二维数组的每个元素运行指定方法并返回。

使用Math.max.apply()方法来获取数组的子数组元素个数的最大长度,使用Array.map()来把每一个元素创建成一个数组。然后使用Array.reduce()Array.forEach()去把组里的元素分别加到各自的数组中。然后再结合 Array.map()和ES6扩展运算符把前面生成的二维数组的每个元素分别调用fn方法。

➜  code cat unzipWith.js
const unzipWith = (arr, fn) =>
    arr.reduce((acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
        Array.from({
            length: Math.max(...arr.map(x => x.length))
        }).map(x => [])
    )
    .map(val => fn(...val));

console.log(unzipWith([[1, 10, 100], [2, 20, 200]], (...args) => args.reduce((acc, v) => acc + v, 0)));

➜  code node unzipWith.js
[ 3, 30, 300 ]

unzipWith就比unzip多了一个对每个二维数组元素调用指定fn方法。即map(val => fn(...val))。其它都和unzip一样,没啥可说的了。看以上例子运行结果就知道了。


supermao
85 声望7 粉丝

you are what you read