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)