Извлечь дочерние массивы из вложенных массивов

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

Примечание. Мне бы очень хотелось понять, почему это происходит и как избавиться от него в моей функции, а не только .filter(arr => arr.length) в моем списке результатов.

Это моя попытка:

var arrs = [
  [1, 2, [3, 4], 5],
  [6, [7, 8, 9, [10, 11]]],
  [12, 13],
  [[14, 15], [16, 17]],
  [[1], 4, [1, 1], 4]
];

// Desired Output
// [
//   [1, 2, 5],
//   [3, 4],
//   [6],
//   [7, 8, 9],
//   [10, 11],
//   [12, 13],
//   [14, 15],
//   [16, 17],
//   [4, 4]
//   [1]
//   [1, 1]
// ]

function extractArrays (arr) {
  return arr.reduce((res, curr) => {
    if (Array.isArray(curr)) {
      res = res.concat(extractArrays(curr));
    }
    else {
      res[0].push(curr);
    }
    return res;
  }, [[]]);
}

console.log(extractArrays(arrs));
// Results:
// [ 
//   [],  <-- Where is this coming from?
//   [ 1, 2, 5 ],
//   [ 3, 4 ],
//   [ 6 ],
//   [ 7, 8, 9 ],
//   [ 10, 11 ],
//   [ 12, 13 ],
//   [],  <-- Also here
//   [ 14, 15 ],
//   [ 16, 17 ],
//   [ 4, 4 ],
//   [ 1 ],
//   [ 1, 1 ]
// ]
.as-console-wrapper {
  max-height: 100% !important;
}

Ответ 1

Элемент, подобный [[14, 15], [16, 17]], представит [] после рекурсии. Это нужно обработать путем проверки длины.

var arrs = [
  [1, 2, [3, 4], 5],
  [6, [7, 8, 9, [10, 11]]],
  [12, 13],
  [[14, 15], [16, 17]],
  [[1], 4, [1, 1], 4]
];

function extractArrays (arr, acc=[]) {
  if (arr.length == 0 ) return acc;
  let pure = arr.filter(elm => !Array.isArray(elm));
  if (pure.length > 0) {
    acc.push(pure);
  }
    
  acc.concat(arr.filter(elm => Array.isArray(elm)).map(elm => extractArrays(elm, acc)));

  return acc;
}

console.log(extractArrays(arrs));

Ответ 2

Вы можете попробовать следующий код

var arrs = [
  [1, 2, [3, 4], 5],
  [6, [7, 8, 9, [10, 11]]],
  [12, 13],
  [
    [14, 15],
    [16, 17]
  ], // <-- added additional test case
  [
    [1], 4, [1, 1], 4
  ]
];

function extractArrays(arr) {
  return arr.reduce((res, curr, i) => {
    if (Array.isArray(curr)) {
      res = res.concat(extractArrays(curr));
    } else {
        let index = 0;
        for (let j = 0; j <= i; j++) {
          if (!Array.isArray(arr[j])) {
            res[index] ? res[index].push(curr) : res.push([curr]);
            break;
          } else {
            index++;
          }
        }          
    }
    return res;
  }, []); // <-- no initial empty array inside here
}

console.log(extractArrays(arrs));

Ответ 3

Вы можете проверить его при возврате из функции. stackblitz

function extractArray(arr) {
  const res = arr.reduce((res, curr) => {
    if(!Array.isArray(curr)){
      return [[...res[0], curr], ...res.slice(1)]
    }
    return [...res, ...extractArray(curr)]
  }, [[]]);

  return res[0].length ? res : res.slice(1);
}

EDIT: более эффективная функция (проверьте связь stackblitz)

function extractFaster(arr) {
  let res = [0];
  function recExtract(arr) {
    let hasNonArrayElm = false;
    let index = res.length -1;
    arr.forEach(curr => {
      if (!Array.isArray(curr)) {
        hasNonArrayElm ? res[index].push(curr) : res.splice(index, 0, [curr]);
        hasNonArrayElm = true;
        return;
      }
      recExtract(curr);
    });
  }

  recExtract(arr);
  res.splice(-1, 1)
  return res;
}

Ответ 4

Я просто хотел поделиться своим подходом к этой проблеме, мне понравилось пытаться ее решить, в моем случае я также передал массив методу extractArrays, чтобы упростить сбор и фильтрацию каждого массива внутри параметра arrs.

let result = [];
extractArrays(arrs, result);
console.log(result);

function extractArrays(arr, result) {
  let newResult = arr.reduce((acc, curr) => {
    if (Array.isArray(curr)) {
      extractArrays(curr, result);
    } else {
      acc.push(curr);
    }

    return acc;
  }, []);

  newResult.length && result.push(newResult);
}

Ответ 5

ОБНОВЛЕНИЕ: Ответ под строкой является отличным способом сгладить массивы, но я предложил это, потому что я неправильно понял этот вопрос. Я оставлю это, если кому-то будет полезно это знать, но для того, чтобы вести точный учет, я также обновлю свой ответ для решения проблемы, поставленной в вопросе.

Кажется, принятого ответа достаточно, но я попробую свои силы в этом. Я бы использовал Array.reduce, чтобы покрыть все одним махом, а внутри использовал Array.filter, чтобы отделить обычные элементы от элементов массива, а затем использовал бы оператор распространения ... для вложенных массивов, чтобы все переместилось на один уровень, после рекурсивного вызова одной и той же функции extract для всех вложенных массивов. Честно говоря, объяснение может быть труднее понять, чем код, посмотрите:

const data = [
  [1, 2, [3, 4], 5],
  [6, [7, 8, 9, [10, 11]]],
  [12, 13],
  [[14, 15], [16, 17]],
  [[1], 4, [1, 1], 4]
]

const extractChildArrays = arrs => arrs.reduce((acc, cur) => {
  const nestedArrs = cur.filter(a => Array.isArray(a))
  const normalItems = cur.filter(a => !Array.isArray(a))
  acc.push(normalItems, ...extractChildArrays(nestedArrs))
  return acc
}, [])

console.log(extractChildArrays(data))