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

原文地址: JavaScript30秒, 從入門到放棄之Array(五)

博客地址:JavaScript30秒, 從入門到放棄之Array(五)javascript

水平有限,歡迎批評指正java

sampleSize

Gets n random elements at unique keys from array up to the size of array.node

Shuffle the array using the Fisher-Yates algorithm. Use Array.slice() to get the first n elements. Omit the second argument, n to get only one element at random from the array.git

const sampleSize = ([...arr], n = 1) => {
  let m = arr.length;
  while (m) {
    const i = Math.floor(Math.random() * m--);
    [arr[m], arr[i]] = [arr[i], arr[m]];
  }
  return arr.slice(0, n);
};

從給定的數組中隨機選出指定個數的數組元素。github

Fisher-Yates 算法將數組洗牌(打亂順序)。而後使用Array.slice() 來截取數組的前n個元素。若是省略第二個參數n,按n=1處理,即僅取一個隨機元素。算法

➜  code cat sampleSize.js
const sampleSize = ([...arr], n = 1) => {
    let m = arr.length;
    while (m) {
        const i = Math.floor(Math.random() * m--);
        [arr[m], arr[i]] = [arr[i], arr[m]];
    }
    return arr.slice(0, n);
};

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

➜  code node sampleSize.js
[ 2, 1 ]
[ 1, 3, 2 ]
let m = arr.length;
while (m) {
  const i = Math.floor(Math.random() * m--);
  [arr[m], arr[i]] = [arr[i], arr[m]];
}

關鍵點是這個while循環,按數組初始長度m每次遞減1循環,而後結合Math.floorMath.randomm範圍內生成一個隨機索引。用該索引對應的數組元素與索引m-1(即未交換過的最後一個元素)對應數組元素交換位置。循環結束,數組洗牌成功。數組

return arr.slice(0, n);

最後返回被截取的前n個元素。app

shuffle

Randomizes the order of the values of an array, returning a new array.dom

Uses the Fisher-Yates algorithm to reorder the elements of the array.ide

const shuffle = ([...arr]) => {
 let m = arr.length;
 while (m) {
   const i = Math.floor(Math.random() * m--);
   [arr[m], arr[i]] = [arr[i], arr[m]];
 }
 return arr;
};

對指定數組進行隨機排序並返回排好序的新數組。

Fisher-Yates 算法將數組洗牌(打亂順序)並返回排好序的新數組。

➜  code cat shuffle.js
const shuffle = ([...arr]) => {
    let m = arr.length;
    while (m) {
        const i = Math.floor(Math.random() * m--);
        [arr[m], arr[i]] = [arr[i], arr[m]];
    }
    return arr;
};

const foo = [1, 2, 3];
console.log(shuffle(foo));
console.log(foo);

➜  code node shuffle.js
[ 1, 3, 2 ]
[ 1, 2, 3 ]

這就是前面sampleSize用到的算法,注意的是返回的是新的數組,不是在原數組上進行排序的。

similarity

Returns an array of elements that appear in both arrays.

Use Array.filter() to remove values that are not part of values, determined using Array.includes().

const similarity = (arr, values) => arr.filter(v => values.includes(v));

返回一個包含兩個數組的共同元素的數組。

使用Array.filter()結合Array.includes()把一個數組中不屬於第二個數組values的元素都剔除。

➜  code cat similarity.js
const similarity = (arr, values) => arr.filter(v => values.includes(v));

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

filter的主體是第一個數組arr,最終將不知足條件的元素剔除掉。那麼不知足的條件又是什麼呢?

v => values.includes(v)

條件是第一個數組arr不在第二個數組values裏的全部元素,也即僅保留知足values.includes(v)的全部元素。

sortedIndex

Returns the lowest index at which value should be inserted into array in order to maintain its sort order.

Check if the array is sorted in descending order (loosely). Use Array.findIndex() to find the appropriate index where the element should be inserted.

const sortedIndex = (arr, n) => {
  const isDescending = arr[0] > arr[arr.length - 1];
  const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
  return index === -1 ? arr.length : index;
};

返回一個元素應該插入到已知排好序的數組而且不改變該數組排序方式的最小索引。

檢查一個數組是否已經按降序排列(鬆散的),而後使用Array.findIndex()去找出指定的元素應該插在數組中合適位置的索引並返回。

➜  code cat sortedIndex.js
const sortedIndex = (arr, n) => {
  const isDescending = arr[0] > arr[arr.length - 1];
  const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
  return index === -1 ? arr.length : index;
};

console.log(sortedIndex([5, 3, 2, 1], 4));
console.log(sortedIndex([30, 50], 40));

➜  code node sortedIndex.js
1
1

之因此叫sortedIndex是由於數組已經排序了,但升序降序未知。爲此只需比較數組第一個元素arr[0]和數組最後一個元素arr[arr.length - 1]的大小便可判斷升降序。

const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));

這裏有一個三元運算表達式:

isDescending ? n >= el : n <= el

若是是降序的,判斷數組arr元素el是否小於或者等於指定元素n,也就是說,當知足指定數組元素n首次大於或者等於arr遍歷過程當中任意一個元素el的時候,就返回此時的索引。不然判斷數組arr元素el是否大於或者等於指定元素n,尋找過程與前邊相似。

return index === -1 ? arr.length : index;

最後,判斷所找索引index是否爲-1,如果,說明narr全部元素要小,應該放在arr最後一位,即index = arr.length;不然直接返回索引index

sortedIndexBy

Returns the lowest index at which value should be inserted into array in order to maintain its sort order, based on a provided iterator function.

Check if the array is sorted in descending order (loosely). Use Array.findIndex() to find the appropriate index where the element should be inserted, based on the iterator function fn.

const sortedIndexBy = (arr, n, fn) => {
 const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
 const val = fn(n);
 const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el)));
 return index === -1 ? arr.length : index;
};

返回一個元素應該插入到已知排好序的數組而且不改變該數組排序方式的最小索引,前提是受一個指定的方法支配。

檢查一個數組是否已經按降序排列(鬆散的),而後使用Array.findIndex()去找出指定的元素應該插在數組中合適位置的索引並返回,前提是受一個指定的方法支配。

➜  code cat sortedIndexBy.js
const sortedIndexBy = (arr, n, fn) => {
  const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
  const val = fn(n);
  const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el)));
  return index === -1 ? arr.length : index;
};

console.log(sortedIndexBy([{ x: 3 }, { x: 4 }, { x: 5 }], { x: 4 }, o => o.x));

➜  code node sortedIndexBy.js
1

sortedIndexBysortedIndex字面上只差一個By,通常而言By基於xx的意思。

這裏就是基於某個方法fn,意即在findIndex找合適元素的時候指的是對元素調用fn後的結果來進行比較,而不是元素自己。另外要注意的是,判斷原數組arr升降序是也是fn調用結果的升降序,而不是元素自己的升降序。

sortedLastIndex

Returns the highest index at which value should be inserted into array in order to maintain its sort order.

Check if the array is sorted in descending order (loosely). Use Array.map() to map each element to an array with its index and value. Use Array.reverse() and Array.findIndex() to find the appropriate last index where the element should be inserted.

const sortedLastIndex = (arr, n) => {
 const isDescending = arr[0] > arr[arr.length - 1];
 const index = arr
   .map((val, i) => [i, val])
   .reverse()
   .findIndex(el => (isDescending ? n <= el[1] : n >= el[1]));
 return index === -1 ? 0 : arr.length - index - 1;
};

返回一個元素應該插入到已知排好序的數組而且不改變該數組排序方式的最大索引(從右邊數的最小索引)。

檢查一個數組是否已經按降序排列(鬆散的),而後使用Array.map()把原數組map一個包含索引和元素值的二維數組,在此map後的數組上用Array.findIndex()去找出指定的元素應該插在數組從右邊數的合適位置的索引並返回。

➜  code cat sortedLastIndex.js
const sortedLastIndex = (arr, n) => {
    const isDescending = arr[0] > arr[arr.length - 1];
    const index = arr
        .map((v, i) => [i, v])
        .reverse()
        .findIndex(el => (isDescending ? n <= el[1] : n >= el[1]));

    return index === -1 ? 0 : arr.length - index;
};

console.log(sortedLastIndex([10, 20, 30, 30, 40], 30));

➜  code node sortedLastIndex.js
4
const index = arr
  .map((v, i) => [i, v])
  .reverse()
  .findIndex(el => (isDescending ? n <= el[1] : n >= el[1]));

首先用原數組arrmap一個二維數組,至於爲何必定要去map待會再講。

而後reverse()去把map後的二維數組翻轉。

最後在翻轉數組基礎上去查找對應索引:

// sortedLastIndex
isDescending ? n <= el[1] : n >= el[1]

// sortedIndex
isDescending ? n >= el : n <= el

這裏不難看出sortedLastIndexsortedIndex的三元運算表達式是不同的。

  • sortedLastIndex此時是對一個二維數組進行findIndex查找,二維數組的第二個元素即el[1]纔是值,因此要跟它比較。
  • sortedLastIndex的二維數組是通過翻轉的,即若是原本數組是降序的,翻轉後爲升序,因此應該找元素n首次知足小於或等於數組元素el[1]的索引,而sortedIndex顯然相反。

最終再對找出來的是索引index進行判斷,若是index === -1,那麼應該返回0,怎麼講?

對於-1來講,從右邊數起,把整個數組數完了,都沒有找到能放它的位置,那麼只有數組第一個位置符合它。

若是index !== -1,也就是找到一個合適的索引,可是這個index是從右邊數的,轉換成從左邊數的索引就是arr.length - index,(原文是arr.length - index - 1)我認爲是錯的。舉個例子:

arr = [5, 4, 3, 3, 2, 1]
要把3插入數組arr的從右邊數是2;
從左邊數是4,便是:6 - 2 = 4

接着前面說的爲何必定須要mapmap是返回一個新的數組而不改變原數組,arr.reverse()是會直接改變原數組arr的,arr.map(fn).reverse()則不會改變arr而只是改變了arr.map(fn)後返回的數組。

然而我我的以爲map的時候不必返回一個二維數組,直接一維數組就能夠了,由於後續根本用不到map後的第一個元素el[0]即它對應的索引。

如:

➜  code cat sortedLastIndexSave.js
const sortedLastIndex = (arr, n) => {
    const isDescending = arr[0] > arr[arr.length - 1];
    const index = arr
        .map(v => v)
        .reverse()
        .findIndex(el => (isDescending ? n <= el : n >= el));

    return index === -1 ? 0 : arr.length - index - 1;
};

const arr = [10, 20, 30, 30, 40];
console.log(sortedLastIndex(arr, 30));
console.log(arr);

➜  code node sortedLastIndexSave.js
3
[ 10, 20, 30, 30, 40 ]

結果同樣,而且原數組arr並未改變。

sortedLastIndexBy

Returns the highest index at which value should be inserted into array in order to maintain its sort order, based on a provided iterator function.

Check if the array is sorted in descending order (loosely). Use Array.reverse() and Array.findIndex() to find the appropriate last index where the element should be inserted, based on the iterator function fn..

const sortedLastIndexBy = (arr, n, fn) => {
  const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
  const val = fn(n);
  const index = arr
    .map((val, i) => [i, fn(val)])
    .reverse()
    .findIndex(el => (isDescending ? val <= el[1] : val >= el[1]));
  return index === -1 ? 0 : arr.length - index;
};

返回一個元素應該插入到已知排好序的數組而且不改變該數組排序方式的最大索引(從右邊數的最小索引),前提是受一個指定的方法支配。

檢查一個數組是否已經按降序排列(鬆散的),而後使用Array.reverse()Array.findIndex()去找出指定的元素應該插在數組從右邊數的合適位置的索引並返回,前提是受一個指定的方法支配。

➜  code cat sortedLastIndexBy.js
const sortedLastIndexBy = (arr, n, fn) => {
    const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
    const val = fn(n);
    const index = arr
        .map((val, i) => [i, fn(val)])
        .reverse()
        .findIndex(el => (isDescending ? val <= el[1] : val >= el[1]));

    return index === -1 ? 0 : arr.length - index;
};

console.log(sortedLastIndexBy([{ x: 4 }, { x: 5 }], { x: 4 }, o => o.x));
console.log(sortedLastIndexBy([{ x: 40 }, { x: 30 }, { x: 30 }, { x: 20 }, { x: 10 }], { x: 30 }, o => o.x));
console.log(sortedLastIndexBy([{ x: 10 }, { x: 20 }, { x: 30 }, { x: 30 }, { x: 40 }], { x: 30 }, o => o.x));

➜  code node sortedLastIndexBy.js
1
3
4

這個跟前面的sortedLastIndex差異其實就是是否調用fn後再比較的問題,沒啥可說的了。

symmetricDifference

Returns the symmetric difference between two arrays.

Create a Set from each array, then use Array.filter() on each of them to only keep values not contained in the other.

const symmetricDifference = (a, b) => {
  const sA = new Set(a),
    sB = new Set(b);
  return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];
};

返回兩個數組中對稱不相同的元素(簡單說就是數組a中不存在於數組b中的全部元素加上數組b中不存在於數組a中的全部元素)。

對兩個數組分別建立其集合。而後分別使用Array.filter()過濾出不存在於對方的全部元素。

➜  code cat symmetricDifference.js
const symmetricDifference = (a, b) => {
  const sA = new Set(a),
    sB = new Set(b);
  return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];
};

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

➜  code node symmetricDifference.js
[ 3, 4 ]

其實前面的解釋就已經很清楚了。主要看return那行:

return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];

a.filter(x => !sB.has(x))這裏保留a數組裏全部不在b數組的集合裏的元素,加上展開運算符把這些元素拼接成數組,同理,對b數組也同樣。

symmetricDifferenceBy

Returns the symmetric difference between two arrays, after applying the provided function to each array element of both.

Create a Set by applying fn to each array's elements, then use Array.filter() on each of them to only keep values not contained in the other.

const symmetricDifferenceBy = (a, b, fn) => {
  const sA = new Set(a.map(v => fn(v))),
    sB = new Set(b.map(v => fn(v)));
  return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))];
};

返回兩個數組中對稱不相同的元素,前提是對兩個數組全部元素調用指定方法後。(簡單說就是數組a中不存在於數組b中的全部元素加上數組b中不存在於數組a中的全部元素,前提是調用指定方法後)。

對兩個數組分別調用指定方法後建立其集合。而後分別使用Array.filter()過濾出不存在於對方的

➜  code cat symmetricDifferenceBy.js
const symmetricDifferenceBy = (a, b, fn) => {
    const sA = new Set(a.map(v => fn(v))),
        sB = new Set(b.map(v => fn(v)));
    return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))];
};

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

➜  code node symmetricDifferenceBy.js
[ 1.2, 3.4 ]

symmetricDifference的不一樣點在於多了一個fn,因此在建立集合以前,要先對數組元素map一個對全部元素運用fn的方法。再各自過濾的時候也一樣使用fn對數組元素進行調用。

symmetricDifferenceWith

Returns the symmetric difference between two arrays, using a provided function as a comparator.

Use Array.filter() and Array.findIndex() to find the appropriate values.

const symmetricDifferenceWith = (arr, val, comp) => [
  ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1),
  ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1)
];

返回兩個數組中對稱不相同的元素,前提是使用一個指定的比較方法進行比較。

使用Array.filter()Array.findIndex()來找出符合條件的全部元素。

➜  code cat symmetricDifferenceWith.js
const symmetricDifferenceWith = (arr, val, comp) => [
  ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1),
  ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1),
];

const res = symmetricDifferenceWith(
  [1, 1.2, 1.5, 3, 0],
  [1.9, 3, 0, 3.9],
  (a, b) => Math.round(a) === Math.round(b)
);

console.log(res);
➜  code node symmetricDifferenceWith.js
[ 1, 1.2, 3.9 ]

這個就是對數組arr,過濾出對數組arrval全部元素調用comp方法後結果不一樣的全部元素。同理對數組val也同樣。而後二者的結果拼接成最終的結果。

相關文章
相關標籤/搜索