Reduce array of objects with deeply literals keys in JS - javascript

I'm confused about array transform with reduce method. I can't figure out how to deal with nested object with literal keys.
To brings some advantage, I'll post some example I'm wrote and it work fine:
// consider flat array of objects
const names = [
{ name: "Jaden", sname: "Smith", age: 33 },
{ name: "Will", sname: "Smith", age: 12 },
{ name: "Jada", sname: "Smith", age: 29 },
{ name: "Jose", sname: "Varho", age: 21 },
{ name: "Josephina", sname: "Varho", age: 44 },
{ name: "Keanu ", sname: "Reeves", age: 44 }] ;
// suppose i need to transform that array to this shape:
/* {
"Smith": {
"Jaden": 33,
"Will": 12,
"Jada": 29
},
"Varho": {
"Jose": 21,
"Josephina": 44
},
"Reeves": {
"Keanu ": 44
}
}
*/
// this reducer do it's fine:
const shapeIt = (acc, item) => {
console.log('acc:', JSON.stringify(acc));
acc[item.sname] = { ...acc[item.sname], [item.name]: item.age }
return acc
}
const transformShape= (arr) => {
return arr.reduce((acc, item) => shapeIt(acc, item), {});
}
transformShape(names); //gives required shape
So now, let's imaging i have more complex array like:
const flatArray = [
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_1", "value": "0" },
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_2", "value": "1" },
{ "blockId": "first-block", "sectionId": "second-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "first-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "some-section", "fieldId": "_2", "value": "3" },
{ "blockId": "third-block", "sectionId": "other-section", "fieldId": "_1", "value": "3" }];
// and i strictly need to get this shape of object:
/* {
"first-block": {
"first-section": {
"_1": "0",
"_2": "1"
},
"second-section": {
"_1": "1"
}
},
"second-block": {
"first-section": {
"_1": "1"
},
"some-section": {
"_2": "3"
}
},
"third-block": {
"other-section": {
"_1": "3"
}
}
}
*/
At this moment I'm write this kind of reduce function. It works but it gives me only last fieldId keys of each section in block. if section in one block have more than one fieldId - it lose it. I'm watch for a accumulator, and see that only keys with different blockId, sectionId is accumulated, but not different fieldId's.
const shapeComplex = (acc, item) => {
console.log('acc:', JSON.stringify(acc));
acc[item.blockId] = { ...acc[item.blockId], [item.sectionId]: { [item.fieldId]: item.value } }
return acc
}
const transformComplex = (arr) => {
console.log('initialArr: ', arr)
return arr.reduce((acc, item) => shapeComplex(acc, item), {});
}
transformComplex(flatArray);
// it gives me shape with only last idField in same section and block:
/*
{
"first-block": {
"first-section": {
"_1": "0"
},
"second-section": {
"_1": "1"
}
},
"second-block": {
"first-section": {
"_1": "1"
},
"some-section": {
"_2": "3"
}
},
"third-block": {
"other-section": {
"_1": "3"
}
}
}
*/
Any help, please.

You could take an array of wanted keys for grouping and take either the value or a new object for the next level.
const
data = [{ blockId: "first-block", sectionId: "first-section", fieldId: "_1", value: "0" }, { blockId: "first-block", sectionId: "first-section", fieldId: "_2", value: "1" }, { blockId: "first-block", sectionId: "second-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "first-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "some-section", fieldId: "_2", value: "3" }, { blockId: "third-block", sectionId: "other-section", fieldId: "_1", value: "3" }],
keys = ['blockId', 'sectionId', 'fieldId'],
result = data.reduce((r, o) => {
keys.reduce(
(q, k, i, { length }) => q[o[k]] ??= i + 1 === length ? o.value : {},
r
);
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Approach by taking all values of the object and assign the last item as value for the most nested object.
This approach relies on the order of the values in each object.
const
data = [{ blockId: "first-block", sectionId: "first-section", fieldId: "_1", value: "0" }, { blockId: "first-block", sectionId: "first-section", fieldId: "_2", value: "1" }, { blockId: "first-block", sectionId: "second-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "first-section", fieldId: "_1", value: "1" }, { blockId: "second-block", sectionId: "some-section", fieldId: "_2", value: "3" }, { blockId: "third-block", sectionId: "other-section", fieldId: "_1", value: "3" }],
result = data.reduce((r, o) => {
const
values = Object.values(o),
value = values.pop();
values.reduce(
(q, k, i, { length }) => q[k] ??= i + 1 === length ? value : {},
r
);
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You also need to copy the properties (fields) of the innermost (section) object:
const shapeComplex = (acc, item) => {
console.log('acc:', JSON.stringify(acc));
acc[item.blockId] = {
...acc[item.blockId],
[item.sectionId]: {
...acc[item.blockId]?.[item.sectionId],
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[item.fieldId]: item.value
}
};
return acc
}
I would however recommend to either always use object spread on every level
const shapeComplex = (acc, item) => {
return {
...acc,
[item.blockId]: {
...acc[item.blockId],
[item.sectionId]: {
...acc[item.blockId]?.[item.sectionId],
[item.fieldId]: item.value
}
}
};
}
or to use mutations:
const shapeComplex = (acc, {blockId, sectionId, fieldId, value}) => {
const block = acc[blockId] ?? (acc[blockId] = {});
const section = block[sectionId] ?? (block[sectionId] = {});
section[fieldId] = value;
return acc;
}
As #Nina showed in here answer, you can generalise this to arbitrarily deep assignments.

You can do the following,
const flatArray = [
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_1", "value": "0" },
{ "blockId": "first-block", "sectionId": "first-section", "fieldId": "_2", "value": "1" },
{ "blockId": "first-block", "sectionId": "second-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "first-section", "fieldId": "_1", "value": "1" },
{ "blockId": "second-block", "sectionId": "some-section", "fieldId": "_2", "value": "3" },
{ "blockId": "third-block", "sectionId": "other-section", "fieldId": "_1", "value": "3" }];
const shapeComplex = (acc, item) => {
if(acc[item.blockId]) {
acc[item.blockId] = { ...acc[item.blockId], [item.sectionId]: {...acc[item.blockId][item.sectionId], [item.fieldId]: item.value } }
} else {
acc[item.blockId] = { ...acc[item.blockId], [item.sectionId]: {[item.fieldId]: item.value } }
}
return acc
}
const transformComplex = (arr) => {
return arr.reduce((acc, item) => shapeComplex(acc, item), {});
}
console.log(transformComplex(flatArray));

You are making a tree from a flat data structure. Specify the tree hierarchy (levels) and
properties to use to create a leaf node data element.
Example:
function treeFrom (arr, levels, name, value) {
return arr.reduce(
(root,item) =>
{
var node = root;
for (let level of levels) /* descend to bottom tier */
{
levelvalue = item[level];
if (! node[levelvalue]) node[levelvalue] = {};
node = node[levelvalue];
}
node[item[name]] = item[value];
return root;
}
,
{}
)
}
console.log ( treeFrom(flatArray, ['blockId', 'sectionId'], 'fieldId', 'value') );

Related

Merge 2 objects in an array where the values are an array

I am trying to merge values in 2 objects from the same array. The objects in this case are similar and the values I want to merge are arrays(Set)
var array = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
Expected Output
[
{
name: "foo1",
value: ["val1","val2", "val3"]
},{
name: "foo2",
value: ["val4","val4", "val5"]
}
]
My Code
var output = [];
array.forEach(function(item) {
var existing = output.filter(function(v, i) {
return v.name == item.name;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
let newValue = new Set(output[existingIndex].value).add(item.value)
output[existingIndex].value = Array.from(newValue);
} else {
output.push(item);
}
});
Output Gotten
[ {
name: "foo1",
value: ["val1", "val2", ["val2", "val3"]]
}, {
name: "foo2",
value: ["val4", ["val4", "val5"]]
}]
How can I get the expected output (ES6 would also be preferred)
Try this
const array = [
{
"name": "foo1",
"value": [
"val1",
"val2",
"val3"
]
},
{
"name": "foo1",
"value": [
"val2",
"val3"
]
},
{
"name": "foo2",
"value": [
"val4",
"val5"
]
},
{
"name": "foo2",
"value": [
"val4",
"val5"
]
}
]
const result = []
for (const item of array) {
const existingItem = result.find(i => i.name === item.name)
if (existingItem) {
existingItem.value = [...new Set([...existingItem.value, ...item.value])]
} else {
result.push(item)
}
}
console.log(result)
Is this code solve your problem?
var array = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
var output = [];
array.forEach(function(item) {
var existing = output.filter(function(v, i) {
return v.name == item.name;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
let newValue = new Set(output[existingIndex].value.concat(item.value))
output[existingIndex].value = Array.from(newValue);
} else {
output.push(item);
}
});
try
var arr = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
var arr2={}
arr.map((elem,ind)=>{
if(!arr2[elem.name]){
arr2[elem.name]=[]
}
arr2[elem.name]=[...arr2[elem.name],...elem.value]
})
arr=Object.keys(arr2);
arr.map((elem,ind)=>{
arr[ind]={name:elem,value:arr2[elem]};
})
You can do the following using reduce,
var array = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
res = array.reduce((prev, curr) => {
let index = prev.findIndex(item => item.name === curr.name);
if(index > -1) {
s = new Set([...prev[index].value, ...curr.value]);
prev[index].value = Array.from(s);
} else {
prev.push(curr);
}
return prev;
},[]);
console.log(res);
You could use reduce method with a Map as accumulator value and then use spread syntax ... on Map values to get an array of values.
var array = [{"name":"foo1","value":["val1","val2","val2","val3"]},{"name":"foo1","value":["val2","val3"]},{"name":"foo2","value":["val4","val4","val5"]},{"name":"foo2","value":["val4","val5"]}]
const map = array.reduce((r, { name, value }) => {
if (!r.has(name)) r.set(name, { name, value })
else r.get(name).value.push(...value)
r.get(name).value = [...new Set(r.get(name).value)]
return r;
}, new Map)
const result = [...map.values()]
console.log(result)
One approach is to create an unique list of keys and iterate over it. Create an array for each key and merge the values. The vanilla js way is:
Array.from(new Set(array.map(el => el.name)))
.map(name => ({
name,
value: Array.from(new Set(array.filter(el => el.name === name).flatMap(el => el.value)))
}))
Example:
const array = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
console.log(Array.from(new Set(array.map(el => el.name)))
.map(name => ({
name,
value: Array.from(new Set(array.filter(el => el.name === name).flatMap(el => el.value)))
})));
Using lodash you can reduce it to
_.uniq(array.map(el => el.name))
.map(name => ({
name,
value: _.uniq(array.filter(el => el.name === name).flatMap(el => el.value))
}))
Example:
const array = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
console.log(_.uniq(array.map(el => el.name))
.map(name => ({
name,
value: _.uniq(array.filter(el => el.name === name).flatMap(el => el.value))
})));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
Find unique values of keys. Match this keys within array and return unique objects. Push this objects in an empty array. Then match other objects value with the new arrays objects value and push the unmatched values to this new array.
var arr = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
let key = [];
arr.map((val)=>key.push(val.name));
let uniquekeys = [...new Set(key)]; //unique values of keys
let newarr = [];
uniquekeys.map((uniquekey,ind)=>{
let reduceunique = arr.filter((vals)=>uniquekey == vals.name); // return matching objects as array
newarr.push(reduceunique[0]); // Push unique objects in an empty array
for(let i = 1; i<uniquekeys.length;i++){
reduceunique[i].value.map((val)=>{
let existvalue = newarr[ind].value.indexOf(val); // Match every value with the unique objects values
if(existvalue<0){
newarr[ind].value.push(val); // push the unmatched value in the array
}
});
};
});
console.log(newarr);
try to use Array.reduce and Array.filter to get the result like the following
var array = [
{
name: "foo1",
value: ["val1","val2"]
},
{
name: "foo1",
value: ["val2", "val3"]
},
{
name: "foo2",
value: ["val4"]
},
{
name: "foo2",
value: ["val4","val5"]
},
];
res = array.reduce((prev, curr) => {
let index = prev.findIndex(item => item.name === curr.name);
if(index > -1) {
prev[index].value = [...prev[index].value, ...curr.value];
prev[index].value = prev[index].value.filter((v,i) => prev[index].value.indexOf(v) === i)
} else {
prev.push(curr);
}
return prev;
},[]);
console.log(res);

Check if an element is common between 2 arrays and then assign values from it

In my angular 8 application, I have 2 arrays:
array1 = [{
"SubType": "2|3|4|5|6",
},
{
"SubType": "2",
},
{
"SubType": "3|4",
},
{
"SubType": "6",
},
{
"SubType": "3|6",
},
]
&
array2 = [{
"id": 2,
"type": "1",
},
{
"id": 3,
"type": "5",
},
{
"id": 4,
"type": "4",
},
{
"id": 5,
"type": "3",
},
{
"id": 6,
"type": "2",
}
]
I am trying to check each "SubType" in array1 and see if that element(id) is present in array2 and if present assign its "type" to a variable. "SubType" is | separated which I and converting to an array using array1..split('|'). This when assigning to a variable will need to be comma separated. I tried using array filter but I am not able to find a way to loop thorough the second array. Can anyone help?
array1.forEach(reg => {
if (reg.SubType) {
let subTypeTemp = reg.SubType.split('|');
let tempVariable = subTypeTemp.some(ele => {
let stringToassign = '';
for (let i = 0; i < array2.length; i++) {
if (ele == array2[i].id) {
stringToassign += array2[i].type + ",";
}
}
})
}
})
const array1 = [
{
SubType: "2|3|4|5|6"
},
{ SubType: "2" },
{ SubType: "3|4" },
{ SubType: "6" },
{ SubType: "3|6" }
];
const array2 = [
{
id: 2,
type: "1"
},
{ id: 3, type: "5" },
{ id: 4, type: "4" },
{ id: 5, type: "3" },
{ id: 6, type: "2" }
];
const array2Obj = array2.reduce(
(acc, curr) => ({
...acc,
[curr.id]: curr.type
}),
{}
);
const types = [];
array1.forEach(item => {
const sub_types = item.SubType.split("|");
sub_types.forEach(st => {
if (st in array2Obj) {
types.push(array2Obj[st]);
}
});
});
const types_str = [...new Set(types)].join(',');
console.log("types", types_str);
You could take a Map and prevent looping array2 over and over for getting type of a wanted id.
var array1 = [{ SubType: "2|3|4|5|6" }, { SubType: "2" }, { SubType: "3|4" }, { SubType: "6" }, { SubType: "3|6" }],
array2 = [{ id: 2, type: "1" }, { id: 3, type: "5" }, { id: 4, type: "4" }, { id: 5, type: "3" }, { id: 6, type: "2" }],
types = new Map(array2.map(({ id, type }) => [id.toString(), type])),
result = array1.map(({ SubType }) => SubType
.split('|')
.map(Map.prototype.get, types)
.join()
);
console.log(result);

Dynamically add objects to an array

I want to create a datasource dynamically for my table from a array of objects.
Required datasource value:
values = [
{
name: "Super Admin"
cv: 1
assessment1_title: score/status
assessment2_title: score/status
interview1_title: score/status
interview2_title: score/status
}
]
I have the following array of object:
data = {
"assessments": [
{
"id": 6,
"title": "PHP Laravel Developer",
"type": "Objective"
},
{
"id": 7,
"title": "Laravel Developer",
"type": "Objective"
}
],
"candidates": [
{
"id": 11,
"user_id": 1,
"user_name": "Super Admin",
"assessments": [
{
"id": 1,
"score": 5,
"duration": 1170,
"status": {
"id": 22,
"name": "completed"
}
},
{
"id": 2,
"score": 0,
"duration": 0,
"status": {
"id": 22,
"name": "Pending"
}
}
]
}
]
}
where the value of assessment_title will be dynamically generated from data.assessments.map(({title}) => title) and the value will be one of score and status
data.canditates.map(res => res.assessments.map(res2=> {
if res2.status.id ==22 {
value = res2.score
} else {
value = res2.status.name
}
})
);
I want to make the required datasource value. Thanks in advance
You can get the desired result by using reduce on candidates and then forEach assessments element add title and score/status.
const data = {"assessments":[{"id":6,"title":"PHP Laravel Developer","type":"Objective"},{"id":7,"title":"Laravel Developer","type":"Objective"}],"candidates":[{"id":11,"user_id":1,"user_name":"Super Admin","assessments":[{"id":1,"score":5,"duration":1170,"status":{"id":22,"name":"completed"}},{"id":2,"score":0,"duration":0,"status":{"id":22,"name":"Pending"}}]}]}
const result = data.candidates.reduce((r, c) => {
const obj = {}
obj.cv = c.user_id;
obj.name = c.user_name;
c.assessments.forEach((e, i) => {
const {score, status: {name}} = e;
const {title} = data.assessments[i];
obj[title] = e.status.id === 22 ? name : score;
})
r.push(obj)
return r;
}, [])
console.log(result)
You could also create a bit more flexible solution that will work in case you have more then two keys in original object.
const data = {"assessments":[{"id":6,"title":"PHP Laravel Developer","type":"Objective"},{"id":7,"title":"Laravel Developer","type":"Objective"}],"interviews":[{"id":1,"title":"Interview 1"},{"id":2,"title":"Interview 2"}],"candidates":[{"id":11,"user_id":1,"user_name":"Super Admin","interviews":[{"id":1,"score":3,"status":{"name":"completed"}},{"id":2,"score":0,"status":{"name":"pending"}}],"assessments":[{"id":1,"score":5,"duration":1170,"status":{"id":22,"name":"completed"}},{"id":2,"score":0,"duration":0,"status":{"id":22,"name":"Pending"}}]}]}
const result = data.candidates.reduce((r, c) => {
const obj = {}
obj.cv = c.user_id;
obj.name = c.user_name;
Object.keys(data).forEach(k => {
if(k !== 'candidates') {
data[k].forEach((e, i) => {
const {title} = e;
const {score, status: {name}} = c[k][i];
obj[title] = `${score}/${name}`
})
}
})
r.push(obj)
return r;
}, [])
console.log(result)
My solution would be something like below.
data = {
assessments: [
{
id: 6,
title: "PHP Laravel Developer",
type: "Objective"
},
{
id: 7,
title: "Laravel Developer",
type: "Objective"
}
],
candidates: [
{
id: 11,
user_id: 1,
user_name: "Super Admin",
assessments: [
{
id: 6,
score: 5,
duration: 1170,
status: {
id: 22,
name: "completed"
}
},
{
id: 7,
score: 0,
duration: 0,
status: {
id: 21,
name: "Pending"
}
}
]
}
]
};
assessments_map = data.assessments.reduce((acc, val) => {
const {id,...rest} = val;
acc[id] = rest;
return acc
}, {});
assessments_map;
a = data.candidates.map((candidate) => {
return {name: candidate.user_name,
...candidate.assessments.reduce((acc, assessment) => {
acc[assessments_map[assessment.id].title] =
assessment.status.id == 22
? assessment.score
: assessment.status.name;
return acc;
}, {})
}
});
console.log(a);

Rename object keys in cartesian output

Sorry for the bad question title, couldn't figure a better one.
I have this array of options:
const options = [
{
display_name: "Size",
_id: "1",
values: [
{
label: "Small",
_id: "1"
},
{
label: "Extra Large",
_id: "2"
}
]
},
{
display_name: "Colors",
_id: "2",
values: [
{
label: "Red",
value: "#ff0000",
_id: "3"
},
{
label: "Green",
value: "#00ff21",
_id: "4"
},
]
}
];
I run this function against it to get Cartesian Product:
const getCartesian = object => {
return Object.entries(object).reduce(
(r, [key, value]) => {
let temp = [];
r.forEach(s =>
(Array.isArray(value) ? value : [value]).forEach(w =>
(w && typeof w === "object" ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [key]: x }))
)
)
);
return temp;
},
[{}]
);
};
This will result in an array of objects in the following format (console.log output):
[{0: Object, 1: Object}, {0: Object, 1: Object}, ...]
The desired output is:
[
{
"option":{
"id":1,
"display_name":"Size"
},
"value":{
"label":"Small",
"id": 1
}
},
{
"option":{
"id":2,
"display_name":"Color",
},
"value":{
"id":5,
"label":"Red"
}
}
...
]
here's the playground and what I've tried so far: https://codesandbox.io/s/8nvwm76nnj
You need to map() at the end to convert array to object.
const options = [
{
display_name: "Size",
_id: "1",
values: [
{
label: "Small",
_id: "1"
},
{
label: "Extra Large",
_id: "2"
}
]
},
{
display_name: "Colors",
_id: "2",
values: [
{
label: "Red",
value: "#ff0000",
_id: "3"
},
{
label: "Green",
value: "#00ff21",
_id: "4"
},
]
}
];
const getCartesian = object => {
let t = Object.entries(object).reduce(
(r, [key, value]) => {
let temp = [];
r.forEach(s =>
(Array.isArray(value) ? value : [value]).forEach(w =>
(w && typeof w === "object" ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [key]: x }))
)
)
);
return temp;
},
[{}]
);
return t.map(({0:val1,1:val2}) => ({option:val1,arr:val2}))
};
console.log(getCartesian(options));
You could wrap the array in an object with a property option. This gives you later an array with objects with option as key for the cartesian product.
const getCartesian = object => {
return Object.entries(object).reduce(
(r, [key, value]) => {
let temp = [];
r.forEach(s =>
(Array.isArray(value) ? value : [value]).forEach(w =>
(w && typeof w === "object" ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [key]: x }))
)
)
);
return temp;
},
[{}]
);
};
const options = [{ display_name: "Size", _id: "1", values: [{ label: "Small", _id: "1" }, { label: "Extra Large", _id: "2" }] }, { display_name: "Colors", _id: "2", values: [{ label: "Red", value: "#ff0000", _id: "3" }, { label: "Green", value: "#00ff21", _id: "4" }] }];
console.log(getCartesian({ option: options }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to find the highest value inside object by two item conditional?

I'm trying to get the highest value of my object by ID and date. To explain better, this is my array:
[
{
"id":"6",
"date":"201901",
"value":"1"
},
{
"id":"6",
"date":"201901",
"value":"12"
},
{
"id":"6",
"date":"201901",
"value":"123"
},
{
"id":"6",
"date":"201901",
"value":"1231"
},
{
"id":"6",
"date":"201902",
"value":"4"
},
{
"id":"6",
"date":"201902",
"value":"45"
},
{
"id":"5",
"date":"201902",
"value":"56"
},
{
"id":"5",
"date":"201902",
"value":"46"
}
]
I'm trying to get this return (Filtering the highest value by id and date):
[
{
"id":"6",
"date":"201901",
"value":"1231"
},
{
"id":"6",
"date":"201902",
"value":"45"
},
{
"id":"5",
"date":"201902",
"value":"56"
}
]
In my code, I'm using map and filter but not works:
let resultados = array
array.map(item => {
resultados.filter(itemFilter => {
if(item.id == itemFilter.id && item.date == itemFilter.date){
if(itemFilter.value > item.value){
return itemFilter.value
}
}
})
})
An similar solutis is this
Someone can help me?
You could take a Map with a combined key of the wanted keys for grouping. Then take the greater value.
var data = [{ id: "6", date: "201901", value: "1" }, { id: "6", date: "201901", value: "12" }, { id: "6", date: "201901", value: "123" }, { id: "6", date: "201901", value: "1231" }, { id: "6", date: "201902", value: "4" }, { id: "6", date: "201902", value: "45" }, { id: "5", date: "201902", value: "56" }, { id: "5", date: "201902", value: "46" }],
result = Array.from(data
.reduce((m, o) => (key =>
!m.has(key) || m.get(key).value < o.value
? m.set(key, o)
: m
)(['id', 'date'].map(k => o[k]).join('|')), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.prototype.reduce :
yourArray.reduce( (accumulator, currentItem) => {
var previouslyGreatestItem = accumulator.find(item => item.id == currentItem.id && item.date == currentItem.date);
if (!previouslyGreatestItem) {
accumulator.push(currentItem);
} else if (previouslyGreatestItem.value < currentItem.value) {
previouslyGreatestItem.value = currentItem.value;
}
return accumulator;
}, []);
var yourArray = [{"id":"6","date":"201901","value":"1"},{"id":"6","date":"201901","value":"12"},{"id":"6","date":"201901","value":"123"},{"id":"6","date":"201901","value":"1231"},{"id":"6","date":"201902","value":"4"},{"id":"6","date":"201902","value":"45"},{"id":"5","date":"201902","value":"56"},{"id":"5","date":"201902","value":"46"}];
var result = yourArray.reduce( (accumulator, currentItem) => {
var previouslyGreatestItem = accumulator.find(item => item.id == currentItem.id && item.date == currentItem.date);
if (!previouslyGreatestItem) {
accumulator.push(currentItem);
} else if (previouslyGreatestItem.value < currentItem.value) {
previouslyGreatestItem.value = currentItem.value;
}
return accumulator;
}, []);
console.log(result);

Categories