2

I have an array of objects.
I want to merge the objects into a single array by same key. At that time, I also want to include other value in the array together.
It doesn't matter whether the merged array is an array or an object.

Current array:

[
  {
    "datetime": "2022-01-10",
    "a": 0.5,
    "b": 80.6,
    "c": 1002.2
  },
  {
    "datetime": "2022-01-11",
    "a": 0.7,
    "b": 80.4,
    "c": 1002.4
  },
  {
    "datetime": "2022-01-12",
    "a": 0.4,
    "b": 80.2,
    "c": 1002.3
  }
]

Expected result:

[
  [
    ["2022-01-10", 0.5], ["2022-01-11", 0.7], ["2022-01-12", 0.4]
  ],
  [
    ["2022-01-10", 80.6], ["2022-01-11", 80.4], ["2022-01-12", 1002.4]
  ],
  [
    ["2022-01-10", 1002.2], ["2022-01-11", 1002.4], ["2022-01-12", 1002.3]
  ]
]

or

{
  "a": [
    ["2022-01-10", 0.5], ["2022-01-11", 0.7], ["2022-01-12", 0.4]
  ],
  "b": [
    ["2022-01-10", 80.6], ["2022-01-11", 80.4], ["2022-01-12", 1002.4]
  ],
  "c": [
    ["2022-01-10", 1002.2], ["2022-01-11", 1002.4], ["2022-01-12", 1002.3]
  ]
}

I use forEach() and it works.
But I want to know if there are other ways.

const foo = [[], [], []];
json.forEach((item) => {
  const [a, b, c] = foo;
  a.push([item.datetime, item.a]);
  b.push([item.datetime, item.b]);
  c.push([item.datetime, item.c]);
});
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
ishikawa
  • 21
  • 1
  • these could be helpful? [link1](https://stackoverflow.com/questions/33850412/merge-javascript-objects-in-array-with-same-key) [link2](https://stackoverflow.com/questions/60327038/create-a-new-array-from-object-with-same-keys) – shrys Jan 24 '23 at 07:17
  • How about other entries to each item where the keys are not limited to just `datetime `, `a`, `b` and `c`? – Peter Seliger Jan 24 '23 at 11:17
  • @ishikawa ... Does the OP still participate in this thread? – Peter Seliger Jan 31 '23 at 11:44

5 Answers5

1

You can use reduce function where the initial value will be an empty object. after that, you can check if that object contains that particular key or not and push the data accordingly

a = [
  {
    datetime: "2022-01-10",
    a: 0.5,
    b: 80.6,
    c: 1002.2,
  },
  {
    datetime: "2022-01-11",
    a: 0.7,
    b: 80.4,
    c: 1002.4,
  },
  {
    datetime: "2022-01-12",
    a: 0.4,
    b: 80.2,
    c: 1002.3,
  },
];

const solution = (key) => {
  return a.reduce((acc, { [key]: keyValue, ...next }) => {
    Object.entries(next).forEach(([dataKey, dataValue]) => {
      (acc[dataKey] ??= []).push([keyValue, dataValue]);
    });
    return acc;
  }, {});
};

console.log('Solution 1: ',Object.values(solution("datetime")));
console.log('Solution 2: ',solution("datetime"));
Ronak
  • 359
  • 1
  • 8
  • 1
    `const valueToReturn = { ...a };` ... is not necessary, even not from a *functional programming* point of view. One could just mutate the initially passed value as is and pass it again into the next iteration step of `reduce`. – Peter Seliger Jan 24 '23 at 15:55
  • 1
    ... and why would one use something like `const obj = JSON.parse(JSON.stringify(next)); delete obj[key];` which fully stringifies and parses a datastructure if one could be more to the point with a precise destructuring? ... `a.reduce((result, { [key]: keyValue, ...data }) => { Object.entries(data).forEach(([dataKey, dataValue]) => (result[dataKey] ??= []).push([keyValue, dataValue]) ); return result; }, {});` – Peter Seliger Jan 24 '23 at 16:22
  • 1
    @PeterSeliger agreed with both points, thank you for review and help – Ronak Jan 24 '23 at 17:46
1

As for generic solutions (only the datetime property of each item needs to be known, thus the solutions are agnostic to all other properties) to both of the OP's use cases,

  • the array of arrays where the nested array items are tuples of distinct datetime values and values of same item keys
  • and the object of arrays where the arrays are the same but get referred to by keys which are distinct from datetime,

one easily could use the same reduce based approach only that

  • for the latter result (object based key specific array of arrays) one passes an object as initial value and creates and aggregates the key specific nested arrays upon the currently processed key of each item's rest-property data-entries,

  • whereas for the former result (array of arrays) the initial value is an array where one needs to create and/or access each key specific inner array by the current index of each of the rest-data's values.

const sampleData = [{
  datetime: "2022-01-10",
  a: 0.5,
  b: 80.6,
  c: 1002.2,
}, {
  datetime: "2022-01-11",
  a: 0.7,
  b: 80.4,
  c: 1002.4,
}, {
  datetime: "2022-01-12",
  a: 0.4,
  b: 80.2,
  c: 1002.3,
}];

console.log(

  sampleData
    .reduce((result, { datetime, ...rest }) => {
      Object
        .values(rest)
        .forEach((value, idx) =>
          (result[idx] ??= []).push([datetime, value])
        );
        return result;
    }, [])
);
console.log(

  sampleData
    .reduce((result, { datetime, ...rest }) => {
      Object
        .entries(rest)
        .forEach(([key, value]) =>
          (result[key] ??= []).push([datetime, value])
        );
        return result;
    }, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

In case the key (insertion) order of the original array items can not be guarantied amongst all items, one should use the key-specific second solution, where one would pass the created object to Object.values in order to get an array of arrays ...

const sampleData = [{
  datetime: "2022-01-10",
  a: 0.5,
  b: 80.6,
  c: 1002.2,
}, {
  datetime: "2022-01-11",
  a: 0.7,
  b: 80.4,
  c: 1002.4,
}, {
  datetime: "2022-01-12",
  a: 0.4,
  b: 80.2,
  c: 1002.3,
}];

console.log(

  Object
    .values(
      sampleData
        .reduce((result, { datetime, ...rest }) => {
          Object
            .entries(rest)
            .forEach(([key, value]) =>
              (result[key] ??= []).push([datetime, value])
            );
            return result;
        }, {})
    )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
0

See this it uses map and then returns an array just like your ist

const data = [{
  datetime: "2022-01-10",
  a: 0.5,
  b: 80.6,
  c: 1002.2,
}, {
  datetime: "2022-01-11",
  a: 0.7,
  b: 80.4,
  c: 1002.4,
}, {
  datetime: "2022-01-12",
  a: 0.4,
  b: 80.2,
  c: 1002.3,
}];

const res = data.map((m ,_) => [
  [m.datetime,m.a],
  [m.datetime,m.b],
  [m.datetime,m.c],
]);
console.log({ res });

Or 2nd method because of comments

const data = [{
  datetime: "2022-01-10",
  a: 0.5,
  b: 80.6,
  c: 1002.2,
}, {
  datetime: "2022-01-11",
  a: 0.7,
  b: 80.4,
  c: 1002.4,
}, {
  datetime: "2022-01-12",
  a: 0.4,
  b: 80.2,
  c: 1002.3,
}];

const res = ["a","b","c"].map(key => data.map(obj => [
  obj.datetime,
  obj[key],
]));
console.log({ res });
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Skkk
  • 1
  • 5
  • The result of this approach is not matching the shown in the question. – RAllen Jan 24 '23 at 07:36
  • What does mean that – Skkk Jan 24 '23 at 07:36
  • compare what is expected (shown in the question) and what your code produces. – RAllen Jan 24 '23 at 07:40
  • But i have seen this its equivalent to the first expected output – Skkk Jan 24 '23 at 08:01
  • I have added 2nd method now – Skkk Jan 24 '23 at 08:03
  • Now both are one line functions – Skkk Jan 24 '23 at 08:04
  • What I meant is that your first method produces `[ [ [dt, a], [dt, b], [dt, c] ], [ [dt, a], [dt, b], [dt, c] ], [ [dt, a], [dt, b], [dt, c] ] ]` instead of `[ [ [dt, a], [dt, a], [dt, a] ], [ [dt, b], [dt, b], [dt, b] ], [ [dt, c], [dt, c], [dt, c] ] ]`. The second method produces the correct result but arrays depth 1 level deeper than needed. – RAllen Jan 24 '23 at 08:18
  • @Skkk ... I edited the OP's example codes in order to get/test each result instantly. As *RAllen* already mentioned both results do not meet the OP's specification. – Peter Seliger Jan 24 '23 at 11:27
  • @Skkk ... _"Now both are one line functions"_ ... one-liners are mostly both, hard to read, thus difficult to maintain. – Peter Seliger Jan 24 '23 at 11:32
  • @PeterSeliger. How to add that run button – Skkk Jan 25 '23 at 12:18
  • @Skkk ... [answer on _>>I've been told to create a "runnable" example with "Stack Snippets". How do I do that?<<_](https://meta.stackoverflow.com/a/358993/2627243) – Peter Seliger Jan 25 '23 at 12:26
0

you can use javascript reduce to optimize this:

const data = temp.reduce((acc,{a,b,c,datetime})=>{
  const [first,second,third] = acc;
  first.push([datetime, a]);
  second.push([datetime, b]);
  third.push([datetime, c]);
  return acc;
},[[],[],[]])
Talha Fayyaz
  • 481
  • 2
  • 9
0

const Json = [
    {
        "datetime": "2022-01-10",
        "a": 0.5,
        "b": 80.6,
        "c": 1002.2
    },
    {
        "datetime": "2022-01-11",
        "a": 0.7,
        "b": 80.4,
        "c": 1002.4
    },
    {
        "datetime": "2022-01-12",
        "a": 0.4,
        "b": 80.2,
        "c": 1002.3
    }
]

const func = (arr, i) => {
    let result = [];
    const constants = ["a", "b", "c"];
    for (let index = 0; index < arr.length; index++) {
        result.push([arr[index].datetime, arr[index][`${constants[i]}`]]);
    }
    return result;
}

const result = Json.map((d, i) => {
    return func(Json, i);
});

console.log(result)
Ahmad Faraz
  • 1,371
  • 1
  • 4
  • 11