前端中的簡單程式設計題-陣列(2)

thomaszhou發表於2019-03-01

一邊學習前端,一邊通過部落格的形式自己總結一些東西,當然也希望幫助一些和我一樣開始學前端的小夥伴。

如果出現錯誤,請在評論中指出,我也好自己糾正自己的錯誤

此文是連線我之前的一篇總結文章,可以去我主頁搜尋

前端中的簡單程式設計題-陣列(2)

author: thomaszhou

一到多維陣列的最值求解

  • 1.6.1 一維陣列的最值

  • for迴圈或者foreach(比for快點)

    • 不討論for放啊
  • forEach方法

<script>
//forEach比普通迴圈效率高點
    var arr = [1,23,45,66,75,111,4];
    Array.prototype.max = function () {
      var max = this[0];
      this.forEach (function (eval) {
        if (eval > max) {
          max = eval;
        }
      });
        return max;
    }
    console.log(arr.max());//111
</script>
複製程式碼
  • reduce方法來求解
<script>
//reduce()方法
    Array.prototype.max = function () {
      var max = this[0];
      return this.reduce(function (preValue, curValue) {
//        prevalue是每次迭代的結果
        return preValue > curValue ? preValue : curValue;
      })
    }
    console.log(arr.max());
複製程式碼
  • 用內建函式Math.max()和Math.min()
    • 該方法預設不能直接傳一個陣列物件作為引數
    var arr = [1,23,45,66,75,111,4];
//    內建函式Math.max()和Math.min()的括號裡面不能放陣列物件,而是必須放具體的值,如下:
    console.log(Math.max(arr));//NaN
    console.log(Math.max(1,2,43,5,6,777));//77
    console.log(Math.min(1,2,43,5,6,777));//1
    console.log(Math.max());//沒有引數的時候,-Infinity
複製程式碼

以下方法使用來解決不能傳遞物件引數的問題!

如何解決???
//    需要利用別的方法來引用陣列物件
//法一:Function.prototype.apply()

    console.log(Math.max.apply(Math, arr));//111

法二:
    Array.prototype.max = function () {
      return Math.max.apply(Math, arr)
      //return Math.max.apply({}, arr)這個同上條語句
      //不明白為什麼是{}??
    }
    console.log(arr.max());//111
複製程式碼

法三:ES6方法

//    es6的方法-----...展開運算子
//使陣列中的值在函式呼叫的位置展開
const arrayMax = arr => Math.max(...arr);//最大值
const arrayMin = arr => Math.min(...arr);//最小值

console.log(arrMax(arr));//111
</script>
複製程式碼
  • 1.6.2二維陣列的最值

雙層for迴圈是最簡單的方法,

  • 比較好的思路是
    • step1:先得出每個子陣列的最大值
    • step2:然後找出這些數的最大值
  • 方法一(map,reduce,Math.max()):
var arr2 = [[1,20,5],[3,45,564],[34,12]];
var newMap1 = arr2.map(function (item) {
//map是遍歷最外層,item就是每個子陣列
      return item.reduce(function (preVal, curVal) {
  //對每個子陣列item進行求出最值的操作,並返回
        return preVal > curVal ? preVal: curVal;
      })
    });

console.log(newMap1);//[20,564,34]
//Math.max求出最值
console.log(Math.max(...newMap1));//564
複製程式碼
  • step1: 在外層陣列中使用Array.prototype.map()方法遍歷陣列。

  • step2: 使用map()方法遍歷陣列,會呼叫一個回撥函式,在這個回撥函式中,
    使用reduce()方法對每個子陣列group進行合併,將值返回到一個新陣列中。

  • step3: 而在使用reduce()方法時,同樣會呼叫一個回撥函式,這個回撥函式只做了一件事情,
    就是子陣列中的元素做為比較,如果curVal大於preVal,將會返回current,
    否則返回prev,最終得到每個子陣列中最大值

  • 方法二(最佳)—目前沒看懂

var arr2 = [[1,20,5],[3,45,564],[34,12]];
    function largestOfFour (arr) {
      return arr.map(Function.apply.bind(Math.max, null));
    }
console.log(largestOfFour(arr2)); //[20, 564, 34]
複製程式碼

這個方案,使用Function.bind方法建立一個特殊的回撥函式,就類似於Math.max方法一樣,但其有一個Function.prototype.apply功能,將陣列作為它的引數。

  • 先對主陣列中的每個元素做遍歷,也就是陣列內部的每個子陣列

  • 使用map()方法需要一個回撥函式,用來找出內部每個陣列中的最大值。需要建立一個函式,讓Math.max能接受輸入的陣列工作。換句話說,這是非常簡單而且這樣工作也非常的好,如Math.max([9,43,20,6]);將會返回最大值43

  • Function.prototype.apply方法工作可以接受陣列做為引數,但函式通過呼叫上下文,這事情就有點複雜。例如Math.max.apply(null,[9,43,20,6])將呼叫一個Max.max方法,但這樣的方法找起來不容易。

  • 這裡給Function.prototype.apply方法傳遞了一個null引數,告訴Math.max不需要任何上下文。

  • 因為arr.map()需要一個回撥函式,而不只是一個表示式,我們在Function.bind方法中提供了一個函式

  • 因為Function.prototype.apply是一個靜態方法,類似一個函式物件,我們可以稱之為Function.prototype.apply上繫結了一個Function.prototype.bind。例如: Function.apply.bind

  • 現在可以通過Function.prototype.apply.bind回撥函式指定其上下文,比如在這個示例中的Math.max方法

  • 由於是嵌入到Function.prototype.apply方法,需要一個上下文作為第一個引數,而且這個上下文還是一個虛假的。

  • 所以我們將null作為第二個引數傳遞給Function.prototype.apply.bind,並且繫結一個上下文,這個上下文就是Math.max方法

  • 由於Math.max是獨立於任何上下文的,所以它會忽略Function.prototype.apply方法呼叫的虛假上下文

  • 我們使用Function.prototype.apply.bind(Math.max,null)讓一個新函式接受arr.map值,比如陣列中的子陣列

  • 很巧妙的方法!!!!!!!!也是多位陣列的方法

 var arr2 = [[1,20,5],[3,45,564],[34,12]];
    var new1 = function (arr) {
//      通過join先把陣列裡面的值用,拼接成字串
//        用split將字串拼接為陣列
      return arr.join(`,`).split(`,`);
    }(arr2)
//    console.log(largestOfFour(arr2));
    console.log(Math.max(...new1));//564
複製程式碼
  • 1.3.3 多維陣列最值

上文使用不同的方法實現了從二維陣列中取出子陣列中最大值,並且將這些最大值重新組成一個新陣列,如果延伸一下,取出裡面的最大值時,還需要使用Array.prototype.max函式,函式中通過Math.max.apply({},this)取得最大值。不過如果不是二維陣列,那上述方法將無法取出陣列中最大的值。

  • 而在多維陣列中取最大值,可以通過join()和split()方法組合在一起:
 var arr2 = [333,[3,45,564],[34,12,[1,4000]]];
    var new1 = function (arr) {
//      通過join先把陣列裡面的值用,拼接成字串
//        用split將字串拼接為陣列
      return arr.join(`,`).split(`,`);
    }(arr2)
//    console.log(largestOfFour(arr2));
    console.log(Math.max(...new1));//4000
複製程式碼

陣列內部元素隨機排列

var arr = [1,2,3,4,5,7,8,9]
arr.sort(function () {
return Math.random() - 0.5
})
複製程式碼

計數陣列中值的出現次數

陣列平均數(其實就是陣列求和)

使用reduce()將每個值新增到累加器,初始值為0,總和除以陣列長度。

const average = arr => arr.reduce((pre, cur) => pre + cur, 0) / arr.length;

// 箭頭函式,如果函式只有一條語句,不要加"{}"";"
// average([1,2,3]) -> 2
複製程式碼
計數陣列中值的出現次數

每次遇到陣列中的特定值時,使用reduce()來遞增計數器。

const arrayCount = (arr, value) => {
  let count = 0;
  arr.forEach(val => {
    val === value? count += 1: count += 0;
	});
  return count;
}
console.log(arrayCount([1,1,2,3,4], 1)); // 2
複製程式碼

或者reduce方法

const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);

// countOccurrences([1,1,2,1,2,3], 1) -> 3
複製程式碼

找出陣列間不同項-相同項

找出陣列a中除陣列b以外的值

從b建立一個Set,然後在a上使用Array.filter(),只保留b中不包含的值。

es6: Set結構的has方法,是用來查詢值的

const difference = (a, b) => {
	  let s = new Set(b);
	  return a.filter(x => !s.has(x));
	}
   console.log(difference([1,2,3], [1,2]));//[3]
複製程式碼

找出陣列間不同項-相同項(或者說交集)

上面是 !s.has(x)),如果改成s.has(x)),那就是求陣列a中和陣列b的交集

const difference = (a, b) => {
    let s = new Set(b);
    return a.filter(x => s.has(x));//將!去掉了
  }
  console.log(difference([1,2,3,2], [1,2,4,2]));//[1, 2, 2]
複製程式碼

過濾陣列中的非唯一值

6. 陣列去重、合併陣列去重(和11相似)

  • 將Array.filter()用於僅包含唯一值的陣列。
  • 思路:indexOf和lastIndexOf找的位置相同,那就是唯一的值
const arrayIntersection = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));

// console.log(arrayIntersection([1,2,2,3,4,4,5])); // [1,3,5]
複製程式碼

陣列合並

const arrayConcat = (arr, ...args) => {
    let array = arr.concat(...args);
    return array;
};

console.log(arrayConcat([1], [2,6], 5, [[2],10])); // [1, 2, 6, 5, [2], 10]
複製程式碼

缺點:只能將二維陣列降成一維,如上面[2]就是二維陣列

這就聯絡到深度展開陣列的操作,看下面的flatten深度展開陣列


flatten深度展開陣列

其實就是陣列降維:將巢狀多層的陣列(巢狀可以是任何層數)轉換為只有一層的陣列

  • 方法一:
let arr = [1,[2],[[[3,[10]],4],5]];
	let newArr = [];
  function changeArr(arr){
    for(var i=0;i<arr.length;i++){
      //遍歷arr陣列得每一個元素,這裡也可以用forEach
      if(arr[i] instanceof Array){
        //判斷元素是否為陣列
        changeArr(arr[i])
        //元素為陣列則繼續呼叫changeArr方法遍歷
      }else{
        newArr.push(arr[i])
        //元素不是陣列則可以直接push進新陣列
      }
    }
    //此上的判斷也可以直接用三元表示式
  }
  changeArr(arr);
  console.log(newArr); //  [1, 2, 3, 10, 4, 5]
複製程式碼
  • 方法2(可多維):指定降維層數

使用遞迴去遞減深度。使用 Array.reduce() 和 Array.concat() 來合併元素或陣列。基本情況下,當深度為 1 時停止遞迴。省略第二個引數,展開深度為 1。

const flattenDepth = (arr, depth = 1) =>
  depth != 1 ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), [])
  : arr.reduce((a, v) => a.concat(v), []);
  
// 降2維
console.log(flattenDepth([1,[2],[[[3],4],5]], 2)); // [1,2,[3],4,5]
// 降3維
console.log(flattenDepth([1,[2],[[[3],4],5]], 3)); //  [1, 2, 3, 4, 5]
複製程式碼
  • 方法3(可高維):缺點是得到的值變成了字元形式

陣列中元素僅基本型別的元素或陣列,可以轉換為字串,不存在迴圈引用的情況。

function flatten(arr) { return arr.toString().split(`,`)} 
// arr.toString() 結果是字串形式的1,2,3,4

<!--// 變成字元形式的陣列-->
console.log(flatten([1,[2],[[3]], [[[4]]]])) //["1", "2", "3", "4"]
複製程式碼

解決辦法:最後再遍歷一遍陣列,用Number轉化為數字!


獲得第n個斐波那契數字

斐波那契數列是0、1、1、2、3、5、8、13、21、34…

  • 思路:構建一個長度為n的斐波那契數列,然後將最後一個值返回
這個是迭代方式,遞迴方法太耗時

	function fibonacci(n) {
	  let fibo = [0, 1];
	  if (n < 2) {
	    return n;
		}
	  for (let i = 2; i <= n; i++) {
	    fibo.push(fibo[i-1] + fibo[i-2]);
		}
		return fibo[n];
	}
	console.log(fibonacci(0)); // 0
	console.log(fibonacci(1)); // 1
	console.log(fibonacci(6)); // 8
複製程式碼

一些簡單的應用

陣列去掉最後n個值

其實就是去掉最後n個值,利用arr.slice(0,-1)。以0為起點,去一個是-1,去兩個是-2,以此類推,

const initial = arr => arr.slice(0, -1);

// initial([1,2,3]) -> [1,2]
複製程式碼

用值初始化陣列

使用Array(n)建立所需長度的陣列,fill(v)以填充所需的值,可以忽略value使用預設值0。

const initializeArray = (n, value = 0) => Array(n).fill(value);

// initializeArray(5, 2) -> [2,2,2,2,2]
複製程式碼

Initialize array with range (使用指定範圍來定義陣列)

使用 Array(end-start) 建立一個所需長度的陣列,使用 Array.map() 來填充範圍中的所需值。
你可以省略start,預設值為 0。

const initializeArrayRange = (end, start = 0) =>
  Array.apply(null, Array(end - start)).map((v, i) => i + start);
// initializeArrayRange(5) -> [0,1,2,3,4]
複製程式碼

陣列的最後

返回arr.slice(-1)[0]

const last = arr => arr.slice(-1)[0];
//arr.slice(-1)得到的是陣列[3]
console.log(last([1,2,3])); // 3
複製程式碼

獲取陣列的第 N 個元素

使用 Array.slice() 得到一個包含第一個元素的陣列。
如果索引超出範圍,則返回 []。(譯者注:超過索引返回 undefind)
省略第二個引數 n 來獲取陣列的第一個元素。

const nth = (arr, n=0) => (n>0? arr.slice(n,n+1) : arr.slice(n))[0];
// nth([`a`,`b`,`c`],1) -> `b`
// nth([`a`,`b`,`b`]-2) -> `a`
複製程式碼

相關文章