-1

I have a requirement where I need to flat and deflat an array frequently. So, I solved that with usual way using reduce function like below

let arr = [[1,2], [3,4]] 
let flatendarr = arr.flat(2)
console.log("flat", flatendarr)

let deflatarr =  flatendarr.reduce((pv, cv) => {
     pv.temp.push(cv)    
     if(pv.temp.length == 2) {
          pv.res.push(pv.temp)
          pv.temp = []
     }
     return pv
}, {res: [], temp: []})

console.log("de-flat",deflatarr.res)

Since I need to run this code several times may be 500 to 1000 times I'm concerning about efficiency.

Since for large data I'm facing a delay of 1 to 2 seconds I'm searching Is there any other approach which is more efficient.

Vikas Acharya
  • 3,550
  • 4
  • 19
  • 52
  • Receive the data in an unflattened form. – Andy Mar 09 '23 at 06:26
  • 2
    *"I'm concerning about efficiency"* Is there an actual performance issue? Have you done any tests? – gre_gor Mar 09 '23 at 06:28
  • keep a copy of flattened and unflattened? – cmgchess Mar 09 '23 at 06:31
  • *"I have a requirement where I need to flat and deflat"*: that doesn't sound like a hard requirement, but as your solution to something that could have a different solution. Please clarify why you think this is a **requirement**. – trincot Mar 09 '23 at 06:37
  • @gre_gor I only posted the solution. I'm talking about efficiency which I didn't found in that link. The thing is coz of so many iterations I'm doing there is a 1 to 2 seconds delay for large data. that's why I'm checking efficiency – Vikas Acharya Mar 09 '23 at 06:40
  • 1
    That question has multiple answers with different approaches. You can try them and test them to see which fits your efficiency. There is also an [answer](https://stackoverflow.com/a/24782004) that already did that. – gre_gor Mar 09 '23 at 06:53
  • @gre_gor my issue got resolved after following all the valuable feedback. Thank you all – Vikas Acharya Mar 09 '23 at 07:19

2 Answers2

1

Don't do pushing and reduce

That is not going to be performant.

I assume that you genuinely need to de-flatten, because you won't in practice be starting with the de-flattened array and so won't have the option to simply retain it.

Try a standard loop

  • Create an array for the de-flattened output, of the correct length
  • Loop over the elements of this output array, inserting into each entry the array of relevant values from the flattened array

let arr = [
  [1, 2],
  [3, 4]
]
let flatendarr = arr.flat(2)
console.log("flat", flatendarr)

const deflatarr = Array(Math.floor(flatendarr.length/2 ))
for (let i = 0; i < flatendarr.length/2; i ++) {
  deflatarr[i] = [flatendarr[2 * i], flatendarr[2 * i + 1]]
}

console.log("de-flat", deflatarr)

You seem keen on a functional approach: if so, try .map

let arr = [
  [1, 2],
  [3, 4]
]
let flatendarr = arr.flat(2)
console.log("flat", flatendarr)

const deflatarr = Array(Math.floor(flatendarr.length / 2)).fill(0).map(
  (dummy, i) => [flatendarr[2 * i], flatendarr[2 * i + 1]]
)
console.log("de-flat", deflatarr)

Speed is best tested on large datasets

Try your speed comparison on this dataset:

let flatendarr = Array(50000).fill(0).map((dummy,i)=>1+i)

const deflatarr = Array((flatendarr.length / 2)).map(
  (dummy, i) => [flatendarr[2 * i], flatendarr[2 * i + 1]]
)
console.log("de-flat", deflatarr)

ProfDFrancis
  • 8,816
  • 1
  • 17
  • 26
  • Hi I appreciate your time I tested the performance here https://jsbench.me/khlf0r6pfx/1 you can have a look. As of now I found that the solution what I provided is fastest according to jsbench website. – Vikas Acharya Mar 09 '23 at 06:56
  • 3
    @VikasAcharya If you are concerned with performance on large data, then test on that and not on an array with only 4 items. – gre_gor Mar 09 '23 at 07:01
1

Another approach would be where you don't flatten the array, but provide utility functions for those parts of the logic that expects it to be flat.

Here are some ideas:

const flatAt = (arr, i) => arr[i >> 1][i & 1];
const flatPutAt = (arr, i, val) => arr[i >> 1][i & 1] = val;
const flatLength = (arr) => arr.length * 2;

function* flatValues(arr) {
    for (const pair of arr) yield* pair;
}

// Demo
let arr = [[1,2], [3,4]];

console.log("get");
for (let i = 0, size = flatLength(arr); i < size; i++) {
    console.log(flatAt(arr, i));
}
console.log("double");
for (let i = 0, size = flatLength(arr); i < size; i++) {
    console.log(flatPutAt(arr, i, flatAt(arr, i) * 2));
}
console.log("iter", ...flatValues(arr));

You could take it a step further and create a class for it:

class ArrayAsFlat extends Array {
    flatAt(i) {
        return this[i >> 1][i & 1];
    }
    flatPutAt(i, val) {
        return this[i >> 1][i & 1] = val;
    }
    *flatValues() {
        for (const pair of this) yield* pair;    
    }
    get flatLength() {
        return this.length * 2;
    }
}

// Demo
const arr = new ArrayAsFlat([1,2], [3,4]);

console.log("get");
for (let i = 0, size = arr.flatLength; i < size; i++) {
    console.log(arr.flatAt(i));
}
console.log("double");
for (let i = 0, size = arr.flatLength; i < size; i++) {
    console.log(arr.flatPutAt(i, arr.flatAt(i) * 2));
}
console.log("iter", ...arr.flatValues());

Whether or not this turns out to be more efficient depends on how many actions you have to perform using the additional methods.

If most of your code needs the flat type of access, then you could do the inverse, and store the array as flat from the start and provide methods that will return (or set) values as if the array were nested.

The best solution would be to convert your code so it uses one type of access only, so no conversion is needed.

trincot
  • 317,000
  • 35
  • 244
  • 286