lodash-6 flatten,flattenDeep,flattenDepth

774 阅读2分钟

前言

今天是 lodash 的第 6 篇,今天带来的是 flattenflattenDeep, 和 flattenDepth

Api

_.flatten(array)

Arguments

  1. array  (Array) : The array to flatten.

Returns

(Array) : Returns the new flattened array.

Example

_.flatten([1, [2, [3, [4]], 5]]);
// => [1, 2, [3, [4]], 5]

source

function flatten(array) {
  var length = array == null ? 0 : array.length;
  return length ? baseFlatten(array, 1) : [];
}

核心逻辑只有一个,那就是 baseFlatten

baseFlatten

flattenflattenDeep, flattenDepth 的核心方法都是 baseFlatten

function baseFlatten(array, depth, predicate,result) {
  var index = -1,
      length = array.length;

  predicate || (predicate = isFlattenable);
  result || (result = []);

  while (++index < length) {
    var value = array[index];
    // 判断深度 和 value 是否可以再次展开
    if (depth > 0 && predicate(value)) {
      if (depth > 1) {
        // 可以 展开,并且 depth > 1,需要深度递归value
        baseFlatten(value, depth - 1, predicate,result);
      } else {
      // depth = 1,说明value是这种情况
      // value =  [1,2,3]
        arrayPush(result, value);
      }
    } else  {
        // 不能展开 或者 depth == 0,直接赋值
      result[result.length] = value;
    }
  }
  return result;
}

baseFlatten 需要四个参数,分别是 原数组 array,要拍平的深度 depth,断言 predicate, 结果 result

重点在于这个 while 循环,如果 是 depth > 0 并且 数组中的每一项元素满足 predicate,那么继续判断是否是 depth 是否是 大于 1,如果 大于 1,则需要继续递归,如果不是,则使用 arrayPush

如果不满足上述条件,则直接把 value 放到 result 的最后

graph TD
value[/array的每一个元素value/]
满足条件1{depth>0 && predicate}
满足条件2{depth>1}
Start --> value
value --> 满足条件1
满足条件1 --是-->满足条件2 
满足条件2--是-->递归执行baseFlatten
满足条件2--否-->arrayPush
满足条件1 --否-->添加到result的尾部

predicate很容易想到,就是判断判断元素是否是一个数组

predicate 默认值是 isFlattenable;
来看看这个 isFlattenable

isFlattenable

var spreadableSymbol = Symbol.isConcatSpreadable;

function isFlattenable(value) {
  return Array.isArray(value)  ||
    !!(spreadableSymbol && value && value[spreadableSymbol]);
}

这个 Symbol.isConcatSpreadable 是什么意思?

这个属性代表了 元素是否可以使用 concat,看下文例子

const alpha = ['a', 'b', 'c'];
const numeric = [1, 2, 3];
let alphaNumeric = alpha.concat(numeric);

console.log(alphaNumeric);
// Expected output: Array ["a", "b", "c", 1, 2, 3]

numeric[Symbol.isConcatSpreadable] = false;
alphaNumeric = alpha.concat(numeric);

console.log(alphaNumeric);
// Expected output: Array ["a", "b", "c", Array [1, 2, 3]]

对于类数组 (array-like) 对象,默认不展开。期望展开其元素用于连接,需要设置 Symbol.isConcatSpreadable 为 true

var x = [1, 2, 3];

var fakeArray = {
  [Symbol.isConcatSpreadable]: true,
  length: 2,
  0: "hello",
  1: "world"
}

x.concat(fakeArray); // [1, 2, 3, "hello", "world"]

这个属性如果是 false ,那就是使用 concat 时不展开,元编程的一种;

arrayPush

values 数组中的元素 一个一个 的放入到 array 的尾部

function arrayPush(array, values) {
  var index = -1,
      length = values.length,
      offset = array.length;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

举一个例子

let arr = [1,2,3];
let arr2 = [4,5,6,7];
// 那么
此时 length 是 4
offset 是 3
arr[3 + 0] = arr2[0];
arr[3 + 1] = arr2[1];
arr[3 + 2] = arr2[2];

_.flattenDeep(array)

Example

_.flattenDeep([1, [2, [3, [4]], 5]]);
// => [1, 2, 3, 4, 5]

也就是说,把所有的深度数组都拍平

function flattenDeep(array) {
  var length = array == null ? 0 : array.length;
  return length ? baseFlatten(array, Infinity) : [];
}

只需要把 baseFlatten 中的 depth 设置为 Infinity 即可

那么明白这个道理, flattenDepth(array, [depth=1]) 就是在传入 baseFltten 传入 depth

flattenDepth(array, [depth=1])

var array = [1, [2, [3, [4]], 5]];
 
_.flattenDepth(array, 1);
// => [1, 2, [3, [4]], 5]
 
_.flattenDepth(array, 2);
// => [1, 2, 3, [4], 5]

flattenDepth 中也就是把 depth 格式化然后传入 baseFlatten

function flattenDepth(array, depth) {
  var length = array == null ? 0 : array.length;
  if (!length) {
    return [];
  }
  depth = depth === undefined ? 1 : Math.floor(depth);
  return baseFlatten(array, depth);
}

结尾

这个 baseFlatten 也是一个基础方法,在 lodash 中有很多基础方法,只需要传入不同的参数,然后传入,就可以得到不同的效果,这样的方式很符合函数式编程的思想