JavaScript30秒, 從入門到放棄之Array(六)

supermao發表於2018-02-10

原文地址: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一樣,沒啥可說的了。看以上例子執行結果就知道了。

相關文章