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.