首頁 > 軟體

JS前端面試陣列扁平化手寫flat函數範例

2022-07-12 22:00:46

前言

由於上半年參加了校招的技術面試, 前前後後面了20多個人了, 每次面試都會讓應聘者手寫一下陣列扁平化flat(),但是發現居然沒有一個能夠完成寫出來, 所以打算總結一下如果遇到了陣列扁平化的題目(也可以叫做手動封裝flat()方法),到底應該怎麼寫,怎麼寫可以得更高的分;

話不多說, 接下來我將站在面試官的角度分析所有常見的實現方法, 並附上對應的得分情況: 五星打分制

滿分: ⭐⭐⭐⭐⭐

題目 實現扁平化的方法 封裝 flatten

題目描述:

已有多級巢狀陣列 : [1, [2, [3, [4, 5]]], 6] 將其扁平化處理 輸出: [1,2,3,4,5,6]

什麼是扁平化

定義 : 扁平化就是將多維陣列變成一維陣列,不存在陣列的巢狀

1. ES6 flat

flat(depth) 方法會按照一個可指定的深度遞迴遍歷陣列,並將所有元素與遍歷到的子陣列中的元素合併為一個新陣列返回。

引數:

depth(可選) 指定要提取巢狀陣列的結構深度,預設值為 1

返回值:

返回一個新陣列,包含陣列與提取巢狀陣列的所有元素的新陣列

使用 Infinity,可展開任意深度的巢狀陣列

封裝思路: 使用 es6 自帶 API 處理

 const arr = [1, [2, [3, [4, 5]]], 6]
 function flatten(params) {
   return params.flat(Infinity)
 }
 console.log(flatten(arr));
 // 輸出: [1,2,3,4,5,6]

得分 :

直接使用自帶的方法可以很快的實現, 但是面試官當然不希望就看到這些呀 !

2. toString

如果陣列的項全為數位,可以使用join(),toString()可以利用陣列toString() 轉為字串

function flatten(arr) {
  return arr.toString().split(',').map(item =>parseFloat(item))
}
console.log(flatten(arr));
// 輸出:[ 1, 2, 3, 4, 5, 6 ]

得分 : ⭐ (並不是要考你的陣列的方法呼叫)

3. 使用正則替換

看到巢狀的陣列,如果在字串的角度上看就是多了很多[],如果把它們替換就可以實現簡單的扁平化

function flatten (arr) {
  console.log('JSON.stringify(arr)', typeof JSON.stringify(arr))
  let str= JSON.stringify(arr).replace(/([|])/g, '');
  str = '[' + str + ']';
  arr = JSON.parse(str);
  return arr
}
console.log(flatten(arr))

得分 : ⭐ (並不是要考你的陣列的方法呼叫)

4. 迴圈遞迴

4.1 迴圈 + concat + push

當只有一層巢狀陣列使用push的方式扁平化

[1, [2, 3,4,5,6]]

let result = [];
for (let i = 0; i < arr2.length; i++) {
  result = result.concat((arr2[i]));
}
console.log(result);
[ 1, 2, 3, 4, 5, 6 ]

如果有多層巢狀的陣列就需要使用 遞迴的思想 :

思路

  • 迴圈判斷陣列的每一項是否是陣列: Array.isArray(arr[i])
  • 是陣列就遞迴呼叫上面的扁平化一層的程式碼 result = result.concat(flatten(arr[i]));
  • 不是陣列,直接通過push新增到返回值陣列
function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i])
    }
  }
  return result
}
console.log(flatten(arr));

或者使用forEach 立即執行函數

// 遞迴版本的反巢狀
function flatten(array) {
  var flattend = [];
  (function flat(array) {
    array.forEach(function(el) {
      if (Array.isArray(el)) flat(el);
      else flattend.push(el);
    });
  })(array);
  return flattend;
}

當然迴圈可以更改成forEach迴圈,for of ...等其他迴圈,簡單的迴圈遞迴就能夠一樣的解決啦~

得分: ⭐⭐⭐ (能夠使用遞迴寫出陣列扁平化,缺少控制層級關係)

4.2 增加引數控制扁平化深度

這個可以理解為手寫flat()方法啦~

// forEach 遍歷陣列會自動跳過空元素
const eachFlat = (arr = [], depth = 1) => {
  const result = []; // 快取遞迴結果
  // 開始遞迴
  (function flat(arr, depth) {
    // forEach 會自動去除陣列空位
    arr.forEach((item) => {
      // 控制遞迴深度
      if (Array.isArray(item) && depth > 0) {
        // 遞迴陣列
        flat(item, depth - 1)
      } else {
        // 快取元素
        result.push(item)
      }
    })
  })(arr, depth)
  // 返回遞迴結果
  return result;
}
// for of 迴圈不能去除陣列空位,需要手動去除
const forFlat = (arr = [], depth = 1) => {
  const result = [];
  (function flat(arr, depth) {
    for (let item of arr) {
      if (Array.isArray(item) && depth > 0) {
        flat(item, depth - 1)
      } else {
        // 去除空元素,新增非 undefined 元素
        item !== void 0 && result.push(item);
      }
    }
  })(arr, depth)
  return result;
}

得分: ⭐⭐⭐⭐ (能夠使用遞迴寫出陣列扁平化,可以通過引數控制層級關係)

4.3 巧用 reduce

reduce 方法為陣列中的每個元素按序執行一個reducer函數,每一次執行 reducer 會將先前元素的計算結構作為引數傳入,最後將其結果彙總為單個返回值

引數:

  • callbackFn 一個 reducer 函數,包含四個引數:
  • previousVal :上一次呼叫callbackFn時的返回值,在第一次呼叫時,若指定了初始值initialValue,previousVal 的值就位 initialValue,否則初始值就是為陣列的索引為 0 的元素
  • currentVal:陣列中正在處理的元素,在第一次呼叫時,若指定了初始值,其值則為陣列索引為 0 的元素 array[0],否則為 array[1]
  • currentIndex: 陣列中正在處理的元素的索引,若指定了初始值 initialValue,則起始索引號為 0,否則從索引 1 起始
  • array : 用於遍歷的陣列
  • initialValue(可選) : 作為第一次呼叫 callback 函數時引數 previousValue 的值

返回值: 使用 reducer 回撥函數遍歷整個陣列後的結果

思路:

當我們使用 reduce 來解析第一層陣列,可以得到:

const arr = [1, [[2, 3], 4],5]
const result = arr.reduce((acc, val) => acc.concat(val), []);
console.log(result);
// 輸出: [1,[2,3],4,5]

可以看出上面的程式碼可以扁平化一層陣列,對於多層級巢狀的陣列, 這個時候就需要使用遞迴的思想來解決問題了,再次遍歷陣列,發現陣列元素任然是陣列的時候,再次執行上面扁平化

手寫方法

const arr = [1, [[2, 3], 4],5]
function flatten (arr,deep=1) {
 return   arr.reduce((acc,val) => acc.concat(Array.isArray(val)? flatten(val,deep-1):val),[])
}
console.log(arr, Infinity);
// 輸出:[ 1, 2, 3, 4, 5, 6 ]

得分: ⭐⭐⭐⭐ (能夠使用遞迴寫出陣列扁平化,巧用reduce方法可加分)

4.4 使用 Generator 函數

GeneratorFunction是協程在 ES6 的實現,最大特點就是可以交出函數的執行權(即暫停執行)。

它不同於普通函數,是可以暫停執行的,所以函數名之前要加星號,以示區別。

整個 Generator 函數就是一個封裝的非同步任務,或者說是非同步任務的容器。非同步操作需要暫停的地方,都用 yield 語句註明。Generator 函數的執行方法如下。

構造器生成新的生成器函數

function* flatten(array) {
    for (const item of array) {
        if (Array.isArray(item)) {
            yield* flatten(item);
        } else {
            yield item;
        }
    }
}

得分: ⭐⭐⭐ (使用Generator 函數 進行遞迴)

5. 使用堆疊 stack 避免遞迴

遞迴迴圈都可通過維護一個堆結構來解決

如果不使用遞迴陣列來實現扁平化,可以使用堆疊來解決

深度的控制比較低效,因為需要檢查每一個值的深度

思路:

  • 把陣列通過一個棧來維護
  • 當棧不為空的時候迴圈執行處理
  • pop()將棧尾出棧
  • 如果出棧的元素是陣列,就將該元素解構後每一元素進行入棧操作
  • 出棧的元素不是陣列就push進返回值res
  • 反轉恢復原陣列的順序
var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
function flatten(arr) {
  const stack = [...arr];
  const res = [];
  while (stack.length) {
    // 使用 pop 從 stack 中取出並移除值
    const next = stack.pop();
    if (Array.isArray(next)) {
      // 使用 push 送回內層陣列中的元素,不會改動原始輸入
      stack.push(...next);
    } else {
      res.push(next);
    }
  }
  // 反轉恢復原陣列的順序
  return res.reverse();
}
flatten(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

得分: ⭐⭐⭐⭐ (使用資料結構棧的特性取代遞迴操作,減少時間複雜度)

6.while 迴圈+ some方法

some :方法測試陣列中是不是至少有 1 個元素通過了被提供的函數測試。它返回的是一個 Boolean 型別的值。

思路

通過some來判斷陣列中是否用陣列,通過while不斷迴圈執行判斷, 如果是陣列的話可以使用 拓展運運算元... ... 每次只能展開最外層的陣列,加上contact來減少巢狀層數,

function flatten(arr) {
  while (arr.some(item=&gt; Array.isArray(item))) {
    console.log(...arr)
    arr = [].concat(...arr)
    console.log(arr)
  }
  return arr
}
console.log(flatten(arr));

得分: ⭐⭐⭐⭐ (使用while迴圈取消遞迴操作, 巧用some操作進行判斷)

以上就是JS前端面試陣列扁平化手寫flat函數範例的詳細內容,更多關於JS陣列扁平化flat函數的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com