0

Is it possible to combine map() and concat() in the following code to make it cleaner/shorter?

const firstColumnData = data.map((item: any) => {
 return item.firstColumn;
});

const secondColumnData = data.map((item: any) => {
 return item.secondColumn;
});

const allData = firstColumnData.concat(secondColumnData);

For context, later in the file allData is mapped through to populate data into columns. The specific data depends on which page is calling the component.

Basically, I am wondering if I can skip the declaration of firstColumnData and secondColumn data and assign the value to allData directly. This is an example of how I tried to refactor, but it did not work. (white page, could not render)

const allData = data.map((item: any => { 
return item.firstColumn.concat(item.secondColumn)
});
dcmswim
  • 108
  • 7
  • You can do `data.flatMap(item => [item.firstColumn, item.secondColumn])`, but that of course changes order – Bergi May 20 '22 at 00:39

3 Answers3

4

You can use a single reduce() operation. Arguably, what you save by only having to iterate once, you lose in readability:

const data =[
  {firstColumn: 1, secondColumn: 2},
  {firstColumn: 3, secondColumn: 4}
];

const result = data.reduce((a, {firstColumn, secondColumn}, i, {length}) => {
  a[i] = firstColumn;
  a[i + length] = secondColumn;
  return a;
}, []);

console.log(result);
Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
  • That answers my question, thank you! I agree that this is more difficult to read than the original but I appreciate the response. – dcmswim May 24 '22 at 01:26
0

I agree with Robby that readability is probably the most important part in this.

You could one line this though, as:

const allData = [...data.map(item => item.firstColumn), ...data.map(item.secondColumn)];

but in this case you're still looping twice, so you haven't saved any computation, you've just made it shorter to write.

Brett East
  • 4,022
  • 2
  • 20
  • 31
  • I never said readability is the most important part. If you have to do this for a 10,000 element array on every clock tick, readability goes out the window in favor of performance. Additionally, the reduce operation would scale to more than 2 properties, while still maintaining one iteration. – Robby Cornelissen May 20 '22 at 00:36
  • 1
    @RobbyCornelissen Apologies, I should have said "I agree with Robby in that you're potentially losing readability if you move this to a reduce function" – Brett East May 20 '22 at 00:38
  • 1
    No apologies needed at all. Was just clarifying my stance :) – Robby Cornelissen May 20 '22 at 00:41
0

Looping your current logic

My original answer below is, of course, performant as the proverbial January molasses even though I like the shape of it.

A little testing shows that just putting your current logic into a loop offers about the same or better performance than a generalized version RobbyCornelissen's answer (unless you unroll the loop in the reduce...) and has the benefit of simplicity. It relies on defining an array of column properties to iterate over.

const
  data = [{ firstColumn: 1, secondColumn: 2 }, { firstColumn: 3, secondColumn: 4 }],
  columns = ['firstColumn', 'secondColumn'],
  result = [].concat(...columns.map(col => data.map((item) => item[col])));

console.log(result);
Generalized reduce()

const
  data = [{ firstColumn: 1, secondColumn: 2 }, { firstColumn: 3, secondColumn: 4 }],
  columns = ['firstColumn', 'secondColumn'],
  result = data.reduce((a, item, i, { length }) => {
    for (let j = 0; j < columns.length; j++) {
      a[i + length * j] = item[columns[j]]
    }
    return a
  }, []);

console.log(result);

Zip (original answer)

If the properties are guaranteed to be in order you could 'zip' the Object.values. This will handle any number of properties without explicit desctructuring.

const data = [
  { firstColumn: 1, secondColumn: 2 },
  { firstColumn: 3, secondColumn: 4 }
];

const result = zip(...data.map(Object.values)).flat()

console.log(result)
<script>
/**
 * @see https://stackoverflow.com/a/10284006/13762301
 */
 const zip = (...rows) => [...rows[0]].map((_, c) => rows.map((row) => row[c]));
</script>

But to avoid relying on property order you can still destructure.

const result = zip(...data.map(({ firstColumn, secondColumn }) => [firstColumn, secondColumn])).flat()

see: Javascript equivalent of Python's zip function for more discussion on 'zip'.

pilchard
  • 12,414
  • 5
  • 11
  • 23