0

This is the data structure that I got after doing query database.

[
    { name: 'xx1', date: '2017-09-03', value: 49 },
    { name: 'xx2', date: '2017-10-23', value: 67 },
    { name: 'xx2', date: '2017-12-01', value: 70 },
    ...
]

I want to transform it into:

[
    { name: 'xx1', data: { '2017-09-03': 49 } },
    { name: 'xx2', data: { '2017-10-23': 67, '2017-12-01': 70 } },
    ...
]

That data structure is needed in order to build chart using react-chartkick.

Can anyone give me suggestion to solve this? (Especially with lodash or ramda)

4 Answers 4

2

I would use following approach based on reduce and find (ES6) methods:

const data = [
  { name: 'xx1', date: '2017-09-03', value: 49 },
  { name: 'xx2', date: '2017-10-23', value: 67 },
  { name: 'xx2', date: '2017-12-01', value: 70 }
];

const result = data.reduce((acc, item) => {
  const found = acc.find(a => a.name === item.name);
  if (found) {
     found.data[item.date] = item.value;
  } else {
    acc.push({
      name: item.name,
      data: {
        [item.date]: item.value
      }
    });
  }
  return acc;
}, []);

If you don't want ES6, both methods have lodash versions: reduce, find.

Sign up to request clarification or add additional context in comments.

1 Comment

Fantastic! Many thanks, shishou! I never know about reduce before. Thanks for sharing :)
2

You can accumulate an object with Array#reduce() and return its Object.values():

const data = [
  { name: 'xx1', date: '2017-09-03', value: 49 },
  { name: 'xx2', date: '2017-10-23', value: 67 },
  { name: 'xx2', date: '2017-12-01', value: 70 }
]
const map = data.reduce((acc, { name, date, value }) => {
  if (!(name in acc)) {
    acc[name] = { name, data: {} }
  }

  acc[name].data[date] = value

  return acc
}, {})
const result = Object.values(map)

console.log(result)

This approach avoids using Array#find() which is O(n) time complexity and uses name in acc to check for existence and acc[name] for access, which are both O(1) time complexity, making this approach O(n) overall.

In contrast, the other answer is O(n2) time complexity overall.

Comments

1

For a solution using lodash, you can use its _.map, _.groupBy, and _.merge methods to accomplish this.

I find it's easier to follow using the _.chain style, but you can easily convert it to a normal set of nested calls if you like.

const data = [
    { name: 'xx1', date: '2017-09-03', value: 49 },
    { name: 'xx2', date: '2017-10-23', value: 67 },
    { name: 'xx2', date: '2017-12-01', value: 70 },
]

const result = _.chain(data)
  .map(val => ({name: val.name, data: {[val.date]: val.value}})) // convert objects to desired format
  .groupBy("name")                                               // group objects by name
  .map(v => _.merge(...v))                                       // merge objects in each group
  .value();                                                      // done!
  
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

2 Comments

With the pipeline operator proposal and the partial application proposal you'd be able to do data |> _.map(?, val => ({name: val.name, data: {[val.date]: val.value}})) |> _.groupBy(?, 'name') |> _.map(?, v => _.merge(...v)) which I quite like the style of as well.
Also you can simplify your map function to .map(({ name, date, value }) => ({ name, data: { [date]: value } }))
1

Ramda's R.reduceBy or R.groupBy can help you here. In the example below R.reduceBy groups the objects in the array by their name property and then combines the grouped elements by building up the new object with R.assoc. The remaining combination of R.toPairs, R.map and R.zipObj will get the result into the shape you want.

const fn = R.pipe(
  R.reduceBy((res, {date, value}) => R.assoc(date, value, res), {}, R.prop('name')),
  R.toPairs,
  R.map(R.zipObj(['name', 'data']))
)

const data = [
    { name: 'xx1', date: '2017-09-03', value: 49 },
    { name: 'xx2', date: '2017-10-23', value: 67 },
    { name: 'xx2', date: '2017-12-01', value: 70 }
]

console.log(fn(data))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.