Интервьюер задал ряд вопросов: Реализация плоского метода выравнивания массива (сглаживания)

внешний интерфейс
Интервьюер задал ряд вопросов: Реализация плоского метода выравнивания массива (сглаживания)

предисловие

Некоторое время назад, когда Цю Чжао встречался с Билибили, интервьюер спросил: Как этого добиться?flatметод? На тот момент почерк был не идеальным.Позже я вернулся и пересмотрел его и обнаружил, что интервью требовало, чтобы рукописный массив был сглажен (сплющен).flatИнтервьюеров с методами не мало. К ним относятся: Pinduoduo, Xiaomi, Meituan, Didi, shopee, Youzan и др. почеркflatЭтот метод представляет собой очень простой вопрос на собеседовании, который обычно появляется в письменном тесте или первом раунде собеседований и в основном проверяет способность базового рукописного кода. Учитесь сегодняflatособенность реализацииflatЗатем идите ловить серию вопросов интервьюера и заново изучайте выравнивание массива (выравнивание)flatметод.

Гитхаб ткни меня

Резюме кодаArray.prototype.flat()характеристика

Примечание. Метод выравнивания массиваArray.prototype.flat()Также известно как выравнивание массива, выравнивание массива и уменьшение размерности массива. Эта статья под общим названием: Сведение массивов

const animals = ["🐷", ["🐶", "🐂"], ["🐎", ["🐑", ["🐲"]], "🐛"]];

// 不传参数时,默认“拉平”一层
animals.flat();
// ["🐷", "🐶", "🐂", "🐎", ["🐑", ["🐲"]], "🐛"]

// 传入一个整数参数,整数即“拉平”的层数
animals.flat(2);
// ["🐷", "🐶", "🐂", "🐎", "🐑", ["🐲"], "🐛"]

// Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组
animals.flat(Infinity);
// ["🐷", "🐶", "🐂", "🐎", "🐑", "🐲", "🐛"]

// 传入 <=0 的整数将返回原数组,不“拉平”
animals.flat(0);
animals.flat(-10);
// ["🐷", ["🐶", "🐂"], ["🐎", ["🐑", ["🐲"]], "🐛"]];

// 如果原数组有空位,flat()方法会跳过空位。
["🐷", "🐶", "🐂", "🐎",,].flat();
// ["🐷", "🐶", "🐂", "🐎"]

Array.prototype.flat()Обзор функций

  • Array.prototype.flat()Используется для «выравнивания» вложенных массивов в одномерные массивы. Этот метод возвращает новый массив и не влияет на исходные данные.
  • Когда параметр не передается, слой по умолчанию «выравнивается», вы можете передать целое число, чтобы указать количество слоев, которые вы хотите «сгладить».
  • входящий<=0целых чисел будет возвращен исходный массив, а не «сплющенный»
  • InfinityКогда ключевое слово используется в качестве параметра, независимо от того, сколько уровней вложенности, оно будет преобразовано в одномерный массив
  • Если в исходном массиве есть пустые места,Array.prototype.flat()Пустые слоты будут пропущены.

Интервьюер спрашивал N последовательно

Первый вопрос: реализовать простое выравнивание массиваflatфункция

Во-первых, мы потратим немного времени на изучение того, как реализовать простое выравнивание массива.flatфункцию, подробно представить различные схемы реализации, а затем попытаться уловить серийные вопросы интервьюера.

Реализовать идеи

Как этого добиться, идея очень проста: добиться функции выравнивания массиваflatфункция,Все, что нам нужно сделать, это найти элементы типа массива в массиве и развернуть их. Это для достижения выравнивания массиваflatосновные идеи метода.

С идеей нам нужно решить трудности, которые необходимо преодолеть для реализации этой идеи:

  • Первое, что нужно решить, это перебрать каждый элемент массива;
  • Вторая проблема, которую необходимо решить, — определить, является ли элемент массивом;
  • Третье решение — расширить элементы массива на один слой;

Схема перебора массива

Есть много способов перебрать массив и получить элементы массива,Включая, но не ограничиваясь следующим:

  • for 循环
  • for...of
  • for...in
  • forEach()
  • entries()
  • keys()
  • values()
  • reduce()
  • map()
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }];
// 遍历数组的方法有太多,本文只枚举常用的几种
// for 循环
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}
// for...of
for (let value of arr) {
  console.log(value);
}
// for...in
for (let i in arr) {
  console.log(arr[i]);
}
// forEach 循环
arr.forEach(value => {
  console.log(value);
});
// entries()
for (let [index, value] of arr.entries()) {
  console.log(value);
}
// keys()
for (let index of arr.keys()) {
  console.log(arr[index]);
}
// values()
for (let value of arr.values()) {
  console.log(value);
}
// reduce()
arr.reduce((pre, cur) => {
  console.log(cur);
}, []);
// map()
arr.map(value => console.log(value));

Пока существует метод, который может пройти по массиву, чтобы получить каждый элемент в массиве, это возможное решение.

Схема определения того, что элемент является массивом

  • instanceof
  • constructor
  • Object.prototype.toString
  • isArray
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }];
arr instanceof Array
// true
arr.constructor === Array
// true
Object.prototype.toString.call(arr) === '[object Array]'
// true
Array.isArray(arr)
// true

инструкция:

  • instanceofОператор предполагает, что есть только одна глобальная среда, Если веб-страница содержит несколько фреймов и несколько глобальных сред, если вы передаете массив из одного фрейма в другой, входящий массив будет таким же, как собственный во втором фрейме. Каждый созданный массив имеет свой собственный конструктор. (поэтому в данном случае это будет неточно)

  • typeofОператор, принимающий тип массива, вернетobject

  • так какconstructorМожет быть переопределен, поэтому не гарантируется, что это будет массив.

    const str = 'abc';
    str.constructor = Array;
    str.constructor === Array 
    // true
    

Схема расширения элементов массива на один слой

  • оператор спреда +concat

concat()Этот метод используется для объединения двух или более массивов, а добавление оператора расширения в процессе объединения расширяет слой массивов. Подробности см. в приведенном ниже коде.

  • concat +apply

в основном используютapplyПри привязке области вторым передаваемым параметром является массив или подобный массиву объект, где элементы массива будут переданы как отдельные параметры дляfuncфункция. то есть звонокapplyВ процессе функции входящие массивы будут передаваться в функцию для выполнения один за другим, что представляет собой достаточно слой расширения массива.

  • toString + split

Не рекомендуетсяtoString + splitметод, потому что манипулирование строками опасно и опаснопредыдущая статьяЯ сделал случай, когда манипулировал строками посередине, и многие друзья критиковали меня. Если все элементы массива являются числами,toString +splitЭто выполнимо, и это делается за один шаг.

const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }];
// 扩展运算符 + concat
[].concat(...arr)
// [1, 2, 3, 4, 1, 2, 3, [1, 2, 3, [1, 2, 3]], 5, "string", { name: "弹铁蛋同学" }];

// concat + apply
[].concat.apply([], arr);
// [1, 2, 3, 4, 1, 2, 3, [1, 2, 3, [1, 2, 3]], 5, "string", { name: "弹铁蛋同学" }];

// toString  + split
const arr2 =[1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]]]
arr2.toString().split(',').map(v=>parseInt(v))
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3]

После суммирования трех основных трудностей, которые необходимо решить, мы можем легко добиться версии выравнивания массива.flatфункция.

const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }];
// concat + 递归
function flat(arr) {
  let arrResult = [];
  arr.forEach(item => {
    if (Array.isArray(item)) {
      arrResult = arrResult.concat(arguments.callee(item));   // 递归
      // 或者用扩展运算符
      // arrResult.push(...arguments.callee(item));
    } else {
      arrResult.push(item);
    }
  });
  return arrResult;
}
flat(arr)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

На данный момент поздравляем вас с успехом в том, что интервьюер признал вашу способность разрывать код вручную🎉. Но интервьюеры часто идут дальше этого и продолжают изучать различные способности интервьюируемого.

Второй вопрос: использоватьreduceвыполнитьflatфункция

Я видел много интервьюеров, которым нравится просить интервьюера использовать имя напрямую.reduceреализоватьflatфункция. Интересно, почему? Мы увидим почему во второй половине статьи, когда будем рассматривать случай вакансий массива. На самом деле идея та же.

const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }]

// 首先使用 reduce 展开一层
arr.reduce((pre, cur) => pre.concat(cur), []);
// [1, 2, 3, 4, 1, 2, 3, [1, 2, 3, [1, 2, 3]], 5, "string", { name: "弹铁蛋同学" }];

// 用 reduce 展开一层 + 递归
const flat = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flat(cur) : cur);
  }, []);
};
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

Третий вопрос: идея использования стека для достиженияflatфункция

// 栈思想
function flat(arr) {
  const result = []; 
  const stack = [].concat(arr);  // 将数组元素拷贝至栈,直接赋值会改变原数组
  //如果栈不为空,则循环遍历
  while (stack.length !== 0) {
    const val = stack.pop(); 
    if (Array.isArray(val)) {
      stack.push(...val); //如果是数组再次入栈,并且展开了一层
    } else {
      result.unshift(val); //如果不是数组就将其取出来放入结果数组中
    }
  }
  return result;
}
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }]
flat(arr)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

Четвертый вопрос: управляйте количеством «сплющенных» слоев, передавая целочисленные параметры

// reduce + 递归
function flat(arr, num = 1) {
  return num > 0
    ? arr.reduce(
        (pre, cur) =>
          pre.concat(Array.isArray(cur) ? flat(cur, num - 1) : cur),
        []
      )
    : arr.slice();
}
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }]
flat(arr, Infinity);
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

Пятый вопрос: использоватьGeneratorвыполнитьflatфункция

function* flat(arr, num) {
  if (num === undefined) num = 1;
  for (const item of arr) {
    if (Array.isArray(item) && num > 0) {   // num > 0
      yield* flat(item, num - 1);
    } else {
      yield item;
    }
  }
}
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }]
// 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
// 也就是遍历器对象(Iterator Object)。所以我们要用一次扩展运算符得到结果
[...flat(arr, Infinity)]    
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

Вопрос 6: Реализация переписана на цепочке прототиповflatфункция

Array.prototype.fakeFlat = function(num = 1) {
  if (!Number(num) || Number(num) < 0) {
    return this;
  }
  let arr = this.concat();    // 获得调用 fakeFlat 函数的数组
  while (num > 0) {           
    if (arr.some(x => Array.isArray(x))) {
      arr = [].concat.apply([], arr);	// 数组中还有数组元素的话并且 num > 0,继续展开一层数组 
    } else {
      break; // 数组中没有数组元素并且不管 num 是否依旧大于 0,停止循环。
    }
    num--;
  }
  return arr;
};
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }]
arr.fakeFlat(Infinity)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

Вопрос 7: Рассмотрим случай вакансий массива

С самого начала мы сделали выводflatхарактеристики знаю,flatВыполнение функции пропустит пробел. Большинство методов обработки массивов ES5 пропускают пробелы, в том числе:forEach(), filter(), reduce(), every()иsome()пропустит вакансию.

Таким образом, мы можем использовать вышеуказанные методы для реализации функции плоского пропуска вакансий.

// reduce + 递归
Array.prototype.fakeFlat = function(num = 1) {
  if (!Number(num) || Number(num) < 0) {
    return this;
  }
  let arr = [].concat(this);
  return num > 0
    ? arr.reduce(
        (pre, cur) =>
          pre.concat(Array.isArray(cur) ? cur.fakeFlat(--num) : cur),
        []
      )
    : arr.slice();
};
const arr = [1, [3, 4], , ,];
arr.fakeFlat()
// [1, 3, 4]

// foEach + 递归
Array.prototype.fakeFlat = function(num = 1) {
  if (!Number(num) || Number(num) < 0) {
    return this;
  }
  let arr = [];
  this.forEach(item => {
    if (Array.isArray(item)) {
      arr = arr.concat(item.fakeFlat(--num));
    } else {
      arr.push(item);
    }
  });
  return arr;
};
const arr = [1, [3, 4], , ,];
arr.fakeFlat()
// [1, 3, 4]

Дальнейшее чтение:Поскольку правила обращения с вакансиями весьма непоследовательны, рекомендуется избегать вакансий.

Обработка вакансий в ES5 очень непоследовательна, и в большинстве случаев вакансии игнорируются.

  • forEach(), filter(), reduce(), every()иsome()пропустит вакансию.
  • map()Пустые биты пропускаются, но значение сохраняется.
  • join()иtoString()будет рассматривать вакансии какundefinedundefinedиnullбудет рассматриваться как пустая строка.

ES6 явно преобразует пробелы вundefined.

  • entries(),keys(),values(),find()иfindIndex()будет рассматривать вакансию какundefined.
  • for...ofЦикл перебирает вакансии.
  • fill()Пустые позиции рассматриваются как обычные позиции массива.
  • copyWithin()будут скопированы вместе с вакансиями.
  • оператор спреда (...) также превратит пробелы вundefined.
  • Array.fromМетод преобразует пустое пространство массива вundefined.

Суммировать

Интервьюер проверяет тему написания кода на месте.На самом деле это не просто написание кода.В процессе написания кода вы сталкиваетесь с различными точками знаний и граничными условиями кода. Хотя в большинстве случаев интервьюер не будет так извращаться, простоflatОбычно интервьюер постоянно спрашивает и разрывает несколько версий вручную, но интервьюер часто просит более совершенную версию на основе версии кода, который вы написали. Только если мы успокоимся и заложим прочный фундамент, как бы ни спрашивал интервьюер, мы сможем свободно с этим справляться.flatРеализация точно не будет единственными версиями, перечисленными в тексте.Ввод собственного кода — лучший прогресс, в области комментариев или вissueНапишите свою версию!

Сегодня я узнала, если ты узнаешь на волне беспокойства ставь лайк 👍

Пасхалки в конце статьи

Помню, две недели назад, в воскресенье, около десяти часов вечера, я опубликовал свой первый технический пост в блоге Nuggets. Я получил более 70 лайков в понедельник днем, что очень лестно, и я был очень доволен ожидаемыми 70 лайками. Неожиданно статья набрала 1000+ лайков примерно за 5 дней, а уровень Nuggets поднялся сразу до уровня 3. Я очень рад, что меня все признали. Код той статьи был написан очень плохо по определенной схеме.Я очень благодарен друзьям, которые указали на мои ошибки в комментариях.Я также очень благодарен друзьям, которые поставили мне лайк.Я очень ценю лайки от каждого друзей. В то же время я также очень надеюсь, что смогу подружиться в учебе и жизни со своими друзьями, а также обменяться технологиями и жизнью вместе. Bangtiedan очень рад добавить меня в WeChat и добавить групповой чат ~ Bangtiedan также открыл публичный аккаунт, в основном записывая свои технические статьи и свои жизненные ощущения, а также собирал много обучающих материалов онлайн. Все они размещены в общедоступный аккаунт, вы также можете подписаться на волну по своему желанию, или вы можете приватно пообщаться со мной и подтолкнуть вас~

Рекомендуемое чтение

Сила JSON.stringify(), о которой вы не знали