0

I have a single-level nested array of arrays such as below.

var nestedArrays = [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]];

I flatten this into a single uniform array with the function (by Michal Perlapowski in another question here, and faster than the usual reduce/concat, native flat() is unable to be used due to compatibility issues):

const flatten = function(arr, result = []) {
  for (let i = 0, length = arr.length; i < length; i++) {
    const value = arr[i];
    if (Array.isArray(value)) {
      flatten(value, result);
    } else {
      result.push(value);
    }
  }
  return result;
};
...
merged = flatten(nestedArrays);

so that I get below:

[0,1,2,3,4,5,6,7,8,9]

Now say, I have the value "3", and I wish to find which array from nestedArrays this came from. (In the case of "3" for example, returning the value "0", as it comes from the 0th element array within the nestedArrays). How would I go about this the simplest or most efficiently?

I've tried a few ways to make this work, such as:

1) Converting the existing nested array to a pair of values for each nested value containing both the value itself and the nested array's index within the parent array. Easy but duplicate data, creating an intermediary array, and extra forEach loops:

var nestedArrays2 = [[0,0],[1,0],[2,0],[3,0],[4,1],[5,1],[6,1],[7,2],[8,2],[9,2]];

Then, similarly flatten it, however this will result in:

[0,0,1,0,2,0,3,0,4,1,5,1,6,1,7,2,8,2,9,2]

which can be filtered through for odd values as well, all in all:

merged = flatten(nestedArrays2).filter((num, index) => {
  return index % 2 !== 0;
})

Thus in this case, I know nestedArrays2[merged[i]][1] will return the index of the original nested Array the value at merged[i] was in when it as in nestedArrays.

1a) Branching off the previous but with the same general idea, a separate array can be created with no change to the original nestedArrays. Since we're looping through it already, we might as well take the time to flatten it in the same iteration.

var merged = [];
var nestIndexes = [];
nestedArrays.forEach((element,index){
  element.forEach((e) {
    merged.push(e);
    nestIndexes.push(index);
  });
});

This would result in both merged and nestedIndexes such as below:

merged [0,1,2,3,4,5,6,7,8,9]
nestedIndexes [0,0,0,0,1,1,1,3,3,3]

Thus, if I get the index of a value in merged, I can immediately get the equivalent value from nestedIndexes with nestedIndexes[index]

2) Keeping a separate index reference array

I was thinking perhaps I could keep a separate array that marks index positions in which the nested arrays start, before merging the nested arrays. Simply loop through nestedArrays, get length for each element and add to the previous value such that it will be cumulative:

var nestPositions = [];
nestedArrays.forEach((element, index) {
  nestPositions[index] = (element.length + (nestedArrays[index - 1] || 0) -1); //for when index is 0
})

So, nestPositions will be:

[3,6,9]

the index of the last element at each nested array in nestedArrays.

But in this case, after I get merged[i], I would have to loop over nestPositions array each time to see if less than or equal to i and break, for each element to check.

3) Creating a map with nested array index from nestedArrays.

nestMap = new Map();
nestedArrays.forEach((element, index) => {
  nestMap.set(index, element);
})

I could then get merged as normal, and to get the nested array index of the element from merged[i], I could I suppose, do a forEach on the map, like this:

nestMap.forEach((value, key) => {
  if (value.includes(merged[i]) return key;
}

Anyhow, that's the methods I can think off the top of my head. Which one is the best, are there any better more concise ways of approaching this? Either way, appreciate any help, or even simply for reaching this part haha.

Terrornado
  • 793
  • 3
  • 9
  • 21

1 Answers1

1

Doing a forEach on a Map defeats the purpose of using a Map. But if you create a Map from element value to its array index, you can get your answer directly with map.get(value):

var nestedArrays = [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]];
var merged = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var nestedIndex = new Map();
nestedArrays.forEach((nested, i) => {
  nested.forEach((element, j) => nestedIndex.set(element, i));
});

console.log(nestedIndex.get(merged[0]));
console.log(nestedIndex.get(merged[3]));
console.log(nestedIndex.get(merged[5]));
console.log(nestedIndex.get(merged[9]));
slider
  • 12,810
  • 1
  • 26
  • 42
  • Ah good point! But why not simply create an array instead of a map, `var nestedIndex = []; nestedArrays.forEach((nested, i) => { nested.forEach((element) => nestedIndex.push(i)));});` ? then `console.log(nestedIndex[i])` would give the nestedIndex of merged[i]? – Terrornado Oct 01 '19 at 16:04
  • 1
    @Terrornado that assumes your array elements are in perfect sequential order. What if your element values are say 50, 70, 100 ...? – slider Oct 01 '19 at 16:08
  • I'm not sure what you mean, why would the values in the nestedArrays matter? but yes if nestedArray or nestedIndex were to somehow be swapped around after nestedIndex is created, as they are not relational, it would not reflect the other realtime. But should be good for a temporary intermediary that is not expected to have any other operations performed upon it I think. But I think I'm missing something here..? – Terrornado Oct 01 '19 at 16:14
  • 1
    @Terrornado Ah I see what you mean now. That would work too. But you would have to know `i`. Suppose you only know the element value. You'd have to first compute its index in merged, and only then you can get the value `nestedIndex[i]`. – slider Oct 01 '19 at 16:21