Home > OS >  Transformation of received data
Transformation of received data

Time:09-10

I get data like this

[
    {
        "channel_number": 1,
        "id_channel": "св-1312",
        "consumption": 1231,
        "date": 01
    },
    {
        "channel_number": 1,
        "id_channel": "св-1312",
        "consumption": 1234,
        "date": 02
    },
    {
        "channel_number": 1,
        "id_channel": "св-1312",
        "consumption": 1234,
        "date": 03
    },
    {
        "channel_number": 2,
        "id_channel": "св-1314",
        "consumption": 800,
        "date": 01
    },
    {
        "channel_number": 2,
        "id_channel": "св-1314",
        "consumption": 823,
        "date": 02
    },
    {
        "channel_number": 2,
        "id_channel": "св-1314",
        "consumption": 1233,
        "date": 03
    }
]

I need to make the data for each date be merged into one object.

Like this:

[
  {
    date: 01,
    channels: [
      {
        channel_number: 1
        consumption: 1000
      },
      {
        channel_number: 2
        consumption: 2000
      }
    ]
  },
  {
    date: 02,
    channels: [
      {
        channel_number: 1
        consumption: 1000
      },
      {
        channel_number: 2
        consumption: 2000
      }
    ]
  },
  ...
]

The data comes from the server, so there may be more dates and channels.

I did the following but it doesn't give me what I need:

let newArr = arr.map((el: any, i: any) => {
    return {
      date: el.date,
      channels: [{ channel_number: el.channel_number, consumption: el.consumption }],
    };
  });

I've tried other ways, but it doesn't work. It's just that for the first time such a transformation was needed, but I have no experience

Please, help me.

CodePudding user response:

You can do it with Array.prototype.reduce():

const input = [{
    "channel_number": 1,
    "id_channel": "св-1312",
    "consumption": 1231,
    "date": 01
  },
  {
    "channel_number": 1,
    "id_channel": "св-1312",
    "consumption": 1234,
    "date": 02
  },
  {
    "channel_number": 1,
    "id_channel": "св-1312",
    "consumption": 1234,
    "date": 03
  },
  {
    "channel_number": 2,
    "id_channel": "св-1314",
    "consumption": 800,
    "date": 01
  },
  {
    "channel_number": 2,
    "id_channel": "св-1314",
    "consumption": 823,
    "date": 02
  },
  {
    "channel_number": 2,
    "id_channel": "св-1314",
    "consumption": 1233,
    "date": 03
  }
];

const output = input.reduce((acc, item) => {
  // 1. You check whether the accumulator already contains an object
  //    with the same date of the item you are currently considering
  let accItem = acc.find(_item => _item.date === item.date);

  if (accItem) {
    // 2a. if it does, you update that object by pushing into the `channels` array
    //     the data from your item
    accItem.channels.push({
      channel_number: item.channel_number,
      consumption: item.consumption
    });
  } else {
    // 2b. if it does NOT, you create a new object from the item
    //     and you push it into the accumulator
    acc.push({
      date: item.date,
      channels: [{
        channel_number: item.channel_number,
        consumption: item.consumption
      }]
    })
  }
  
  // 3. finally you return the updater accumulator and repeat point 1 and 2
  //    for the following item in your input array
  return acc
}, []);

console.log(output);

EDIT: Alternative solution to avoid nested loops

Actually, now that I think about it, if your input array can contain a lot of items, using Array.prototype.find() inside Array.prototype.reduce() might not be the best solution in terms of performances because you have nested loops.

Although it is less "linear", this solution might be better performing:

const input = [{
    "channel_number": 1,
    "id_channel": "св-1312",
    "consumption": 1231,
    "date": 01
  },
  {
    "channel_number": 1,
    "id_channel": "св-1312",
    "consumption": 1234,
    "date": 02
  },
  {
    "channel_number": 1,
    "id_channel": "св-1312",
    "consumption": 1234,
    "date": 03
  },
  {
    "channel_number": 2,
    "id_channel": "св-1314",
    "consumption": 800,
    "date": 01
  },
  {
    "channel_number": 2,
    "id_channel": "св-1314",
    "consumption": 823,
    "date": 02
  },
  {
    "channel_number": 2,
    "id_channel": "св-1314",
    "consumption": 1233,
    "date": 03
  }
];

const tempObj = input.reduce((obj, item) => {
  if (obj[item.date]) {
    obj[item.date].channels.push({
      channel_number: item.channel_number,
      consumption: item.consumption
    });
  } else {
    obj[item.date] = {
      date: item.date,
      channels: [{
        channel_number: item.channel_number,
        consumption: item.consumption
      }]
    }
  }

  return obj;
}, {});

const output = Object.values(tempObj);

console.log(output);

If you notice that in production the original solution is too slow, you might want to try this one and see if things improve.

CodePudding user response:

I used reduce instead of group date is defined as number with 0 prefixed, so it gets cut optionaly add a sort if u wish to sort by date

    const data = [
    {

            "channel_number": 1,
            "id_channel": "св-1312",
            "consumption": 1231,
            "date": 01
        },
        {
            "channel_number": 1,
            "id_channel": "св-1312",
            "consumption": 1234,
            "date": 02
        },
        {
            "channel_number": 1,
            "id_channel": "св-1312",
            "consumption": 1234,
            "date": 03
        },
        {
            "channel_number": 2,
            "id_channel": "св-1314",
            "consumption": 800,
            "date": 01
        },
        {
            "channel_number": 2,
            "id_channel": "св-1314",
            "consumption": 823,
            "date": 02
        },
        {
            "channel_number": 2,
            "id_channel": "св-1314",
            "consumption": 1233,
            "date": 03
        }
    ];
    let grouped = data.reduce((group, d) => {
      const { date } = d;
      group[date] = group[date] ?? [];
      group[date].push(d);
      return group;
    }, {});
    let maped = Object.entries(grouped)
        .map(([date, value]) => ({
        date,
        channels: value
            .map(d => ({
            channel_number: d.channel_number,
            consumption: d.consumption
          }))
      }));
    console.log(maped);

CodePudding user response:

How about this, it doesn't use a map but it seems to produce the output you require.

const arr = [
    {
        "channel_number": 1,
        "id_channel": "св-1312",
        "consumption": 1231,
        "date": 01
    },
    {
        "channel_number": 1,
        "id_channel": "св-1312",
        "consumption": 1234,
        "date": 02
    },
    {
        "channel_number": 1,
        "id_channel": "св-1312",
        "consumption": 1234,
        "date": 03
    },
    {
        "channel_number": 2,
        "id_channel": "св-1314",
        "consumption": 800,
        "date": 01
    },
    {
        "channel_number": 2,
        "id_channel": "св-1314",
        "consumption": 823,
        "date": 02
    },
    {
        "channel_number": 2,
        "id_channel": "св-1314",
        "consumption": 1233,
        "date": 03
    }
];  

var newObj = {};
arr.forEach(element => {
    // Check if this date already exists, if not then create it
    if (newObj[element.date] == null) {
        newObj[element.date] = { 'date' : element.date, 'channels' : [] };
    }
    newObj[element.date]['channels'].push({ 'channel_number' : element.channel_number, 'consumption' : element.consumption });
});
console.log(newObj);

  • Related