Функция, которая построила бы массив «игл» для доступа к значениям из вложенного массива?

#javascript #arrays #recursion #multidimensional-array

Вопрос:

У меня есть вложенный массив, содержащий несколько строк на разных уровнях.

 let nested = [
  "ONE",
  [
    "TWO",
    "THREE",
    [
      "FOUR",
      "FIVE"
    ],
    "SIX",
    "SEVEN"
  ],
  [
    "HEIGHT"
  ],
  "NINE"
]
 

У меня есть рабочая функция, которая может перемещаться по моему вложенному массиву для извлечения значения, используя массив индексов в качестве «иглы».:

 const getValueByIndexes = (array,indexes) => {
  const children = array[indexes[0]];

  if(indexes.length > 1){
    return getValueByIndexes(children,indexes.slice(1));
  }else{
    return children;
  }
}

let test = getValueByIndexes(nested,[1,2,1]);

console.log(test); //this IS returning "FIVE", as expected
 

Это хорошо работает; но мой вопрос здесь в том, что мне нужна функция, которая СТРОИЛА БЫ эти иглы на основе моего вложенного массива.

Результат, который я хочу, это:

 [
  [0],//ONE
  [1,0],//TWO
  [1,1],//THREE
  [1,2,0],//FOUR
  [1,2,1],//FIVE
  [1,3],//SIX
  [1,4],//SEVEN
  [2,0],//HEIGHT
  [3],//NINE
]
 

Как я мог бы этого достичь ?
Спасибо!

Ответ №1:

Вы можете использовать повторный подход, проверяя наличие массивов.

 const
    getIndices = array => array.flatMap((v, i) => Array.isArray(v)
        ? getIndices(v).map(a => [i, ...a])
        : [[i]]
    ),
    data = ["ONE", ["TWO", "THREE", ["FOUR", "FIVE"], "SIX", "SEVEN"], ["HEIGHT"], "NINE"],
    result = getIndices(data); 

result.forEach(a => console.log(...a)); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Ответ №2:

Вы также можете использовать функцию рекурсивного генератора:

 function* build_arr(d, c = []){
    for (var i = 0; i < d.length; i  ){
        yield* (!Array.isArray(d[i]) ? [[...c, i]] : build_arr(d[i], [...c, i]))
    }
}
let nested = ['ONE', ['TWO', 'THREE', ['FOUR', 'FIVE'], 'SIX', 'SEVEN'], ['HEIGHT'], 'NINE']
console.log(Array.from(build_arr(nested))) 

Ответ №3:

Ответы от Нины Шольц и Ajax1234 оба замечательные. Но также нетрудно сделать это более общим способом для обработки произвольных объектов и их путей. Вот функция, которую я часто использую для этого:

 const getLeafPaths = (obj) =>
  Object (obj) === obj
    ? Object .entries (obj) .flatMap (
        ([k, v]) => getLeafPaths (v) .map (p => [Array .isArray (obj) ? Number(k) : k, ...p])
      )
    : [[]]

let nested = ["ONE", ["TWO", "THREE", ["FOUR", "FIVE"], "SIX", "SEVEN"], ["HEIGHT"], "NINE"]

console .log (getLeafPaths (nested))
//=> [[0], [1, 0], [1, 1], [1, 2, 0], [1, 2, 1], [1, 3], [1, 4], [2, 0], [3]] 
 .as-console-wrapper {max-height: 100% !important; top: 0} 

Таким образом, это решает эту проблему, но это также полезно для других объектов:

 getLeafPaths ({foo: 1, bar: {baz: [{qux: 2}, {quz: 3}], corge: 4}, grault: [5, 6, 7]})
 

даст:

 [
  ["foo"], 
  ["bar", "baz", 0, "qux"], 
  ["bar", "baz", 1, "quz"], 
  ["bar", "corge"], 
  ["grault", 0], 
  ["grault", 1], 
  ["grault", 2]
]