Home > Net >  How to properly map or reduce an Array of Arrays of objects in javascript?
How to properly map or reduce an Array of Arrays of objects in javascript?

Time:11-12

I have the following array of array of objects that is a result of a CloudWatch query using AWS-SDK-V3 JS:

const respCW = [
  [
    { field: '@timestamp', value: '2022-10-25 15:30:36.685' },
    {
      field: '@message',
      value: 'foo'
    },
    {
      field: '@ptr',
      value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
    }
  ],
  [
    { field: '@timestamp', value: '2022-10-25 15:04:56.155' },
    {
      field: '@message',
      value: 'bar'
    },
    {
      field: '@ptr',
      value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
    }
  ]
]

I would lie to convert that array to the following:

const desiredResp = [
  {
    timestamp: '2022-10-25 15:30:36.685',
    message: 'foo'
  },
  {
    timestamp: '2022-10-25 15:04:56.155',
    message: 'bar'
  }
]

I have created the following code:

if(respCW.length>0){
  respCW.forEach( arrOfObje=> {
    let logObj = {
      timestamp:"",
      message:"",
    }
    arrOfObje.forEach(obj => {
      if(obj.field === '@timestamp' || obj.field === '@message'){
        if(obj.field === '@timestamp'){
          date = new Date(obj.value)
          logObj.timestamp = date.getTime()
        }
        if(obj.field === '@message'){
          logObj.message = obj.value
          newResult.push(logObj)
        }
      }
    })
  })
}

The code is working but I would like to know if there is a way to redesign it using array map or reduce.

CodePudding user response:

By using .map() you can map each inner array to a new object, where you can use .find() to obtain the @timestamp and @message values:

const respCW = [ [ { field: '@timestamp', value: '2022-10-25 15:30:36.685' }, { field: '@message', value: 'foo' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ], [ { field: '@timestamp', value: '2022-10-25 15:04:56.155' }, { field: '@message', value: 'bar' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ] ];

const res = respCW.map(arr => {
  const timestamp = arr.find(obj => obj.field === "@timestamp").value;
  const message = arr.find(obj => obj.field === "@message").value;
  return {timestamp, message};
});
console.log(res);

If you need to handle the case where your inner array won't have a @timestamp or @message, then you can use optional chaining (?.) with the nullish coalescing operator (??) to keep your code from crashing and to default the timestamp/message to an empty string:

const respCW = [ [ { field: '@timestamp', value: '2022-10-25 15:30:36.685' }, { field: '@message', value: 'foo' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ], [ { field: '@timestamp', value: '2022-10-25 15:04:56.155' }, { field: '@message', value: 'bar' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ] ];

const res = respCW.map(arr => {
  const timestamp = arr.find(obj => obj.field === "@timestamp")?.value ?? "";
  const message = arr.find(obj => obj.field === "@message")?.value ?? "";
  return {timestamp, message}
});
console.log(res);

If you know that @timestamp will always be the first object and @message will always be the second, then you can skip the .find() operations and just grab the objects from the inner array's directly. Below I've used destructuring assignment (([ts, msg])) to obtain the array values:

const respCW = [ [ { field: '@timestamp', value: '2022-10-25 15:30:36.685' }, { field: '@message', value: 'foo' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ], [ { field: '@timestamp', value: '2022-10-25 15:04:56.155' }, { field: '@message', value: 'bar' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ] ];

const res = respCW.map(([ts, msg]) => ({timestamp: ts.value, message: msg.value}));
console.log(res);

CodePudding user response:

You can use both map and reduce to refactor the code:

const result = respCW.map(entry => {
  return entry.reduce((log, {field, value}) => {
    if (field === '@timestamp') log.timestamp = value;
    if (field === '@message') log.message = value;
    return log;
  }, { timestamp: '', message: '' });
}, []);

const respCW = [ [ { field: '@timestamp', value: '2022-10-25 15:30:36.685' }, { field: '@message', value: 'foo' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ], [ { field: '@timestamp', value: '2022-10-25 15:04:56.155' }, { field: '@message', value: 'bar' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ] ];

const result = respCW.map(entry => {
  return entry.reduce((log, {field, value}) => {
    if (field === '@timestamp') log.timestamp = value;
    if (field === '@message') log.message = value;
    return log;
  }, { timestamp: '', message: '' });
}, []);

console.log(result);

map allows you to create a new array from an existing one by applying a function one by one on each element. Above, we used it to convert the list of lists into a list of objects.

reduce allows you to summarize an array into a single value (e.g. reducing an array of objects into an object, reducing an array of numbers into their sum). Above, we used it to turn the list of properties into a log object.

CodePudding user response:

Another (more parametric) approach based on the exclusion of a particular field name (here: "@ptr") could be:

const data = [ [ { field: '@timestamp', value: '2022-10-25 15:30:36.685' }, { field: '@message', value: 'foo' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ], [ { field: '@timestamp', value: '2022-10-25 15:04:56.155' }, { field: '@message', value: 'bar' }, { field: '@ptr', value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } ] ];

const res = data.map(d=>
  d.reduce((a,c)=>{
    if(c.field!="@ptr") a[c.field.slice(1)]=c.value;
    return a
  },{})
 )

console.log(res);

CodePudding user response:

may be something like that..

const respCW = 
  [ [ { field: '@timestamp', value: '2022-10-25 15:30:36.685'    } 
    , { field: '@message',   value: 'foo'                        } 
    , { field: '@ptr',       value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } 
    ] 
  , [ { field: '@timestamp', value: '2022-10-25 15:04:56.155'    } 
    , { field: '@message',   value: 'bar'                        } 
    , { field: '@ptr',       value: 'xxxxxxxxxxxxxxxxxxxxxxxxxx' } 
  ] ] 

const resp = respCW.reduce((r,cw) =>
  {
  let logObj = { timestamp: '', message: ''} 
  r.push(logObj)
  cw.forEach(({field,value}) => 
    {
    if (field==='@timestamp' ) logObj.timestamp = value;
    if (field==='@message' )   logObj.message   = value;
    })
  return r  
  },[])
  
console.log(resp)

  • Related