i have an array of objects like below
[
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
]
i want to guet the object with max value with the same "name" and "id"
and push it in a new array ,
the expected output is like this :
[
{value: 5, id: 1, name: "x"},
{value: 8, id: 1, name: "y"},
{value: 3, id: 2, name: "x"},
{value: 4, id: 2, name: "y"},
]
thank you
You can use reduce method to do this,
const data = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
]
const res = data.reduce((prev, curr) => {
const index = prev.findIndex((item) => item.id === curr.id && item.name === curr.name);
if(index > -1) {
const obj = prev[index];
if(obj.value < curr.value) {
prev[index] = {...obj, value: curr.value};
return prev;
}
}
prev.push(curr);
return prev;
}, []);
console.log(res);
Using Array.prototype.reduce, you can group that array using id_name key pair and store the maximum values as follows.
const input = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
];
const groupBy = input.reduce((acc, cur) => {
const key = `${cur.id}_${cur.name}`;
if (!acc[key]) {
acc[key] = cur;
}
if (acc[key].value < cur.value) {
acc[key].value = cur.value;
}
return acc;
}, {});
const output = Object.values(groupBy);
console.log(output);
Reduce is used to return a new value that is basically accumulator (adds on previous value) from all the items in the array. Here we can use it to group items using specific key. As you wrote you want to have items showing a record with biggest value having same id and name, these values can be taken as a key (lets look at them as composite private keys of this object).
On each iteration, we check if there is already an object with that key added to the list, if it wasn't we add the object we are now on (during iteration) or if it was already added if its value is smaller than the current object we are on. If the value is smaller, we override the object with the current one.
In the end, we use JS Object.values method that strips away the keys and returns only the values of the object.
const list = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
];
const groupedResults = list.reduce((result, currentObject) => {
const currentKey = currentObject.id + currentObject.name;
if (!result[currentKey] || result[currentKey].value < currentObject.value) { /* Here we check if object with certain key was assigned to previously or if it was is the value smaller than of the object that we are currently seeing */
result[currentKey] = Object.assign({}, currentObject) //We need to do copy of the object (it can be also done using object destructuring) in order to have a new object that will not be bound by reference with the original one
};
return result;
}, {});
const requestedList = Object.values(groupedResults);
console.log(requestedList)
Related
This question builds on many similar ones like Construct hierarchy tree from flat list with parent field?
However the twist is that there is no parent id.
e.g.
[
{id: 1, depth: 1, ...},
{id: 2, depth: 2, ...},
{id: 3, depth: 3, ...},
{id: 4, depth: 2, ...},
{id: 5, depth: 1, ...},
{id: 6, depth: 2, ...},
{id: 7, depth: 2, ...},
{id: 8, depth: 1, ...},
{id: 9, depth: 2, ...},
{id: 10, depth: 3, ...},
{id: 11, depth: 3, ...},
]
What is a performant way to construct the following tree?
Note that the children always come after the parent i.e. one can see the tree from the depth value. For example, id 2 is a child of id 1 since its depth is 2 and id 1 has a depth of 1. id 3 is a child of id 2 since id 3 has a depth of 3. id 4 is a child of id 1 not id 3 because id 4 has a depth of 2 (a step up) from id 3's depth of 3
\\tree digram
1
2
3
4
5
6
7
8
9
10
11
Should have values like
[
{id:1, depth:1, children: [
{id: 2, depth: 2, children: [...]},
...
]},
{id:5, depth:1, children: [...]},
{id:6, depth:1, children: [...]},
]
You can use an array for this that has an index for each depth. At every moment, it will represent a path from the (virtual) root to the current node. When dealing with a node, its parent will sit at index depth-1, where it can be inserted in that parent's children property, and the node itself will be placed at index depth:
function createForest(flatdata) {
const path = [{ children: [] }];
for (const obj of flatdata) {
path[obj.depth - 1].children.push(path[obj.depth] = { ...obj, children: [] });
}
return path[0].children;
}
// demo
const flatdata = [{id: 1, depth: 1},{id: 2, depth: 2},{id: 3, depth: 3},{id: 4, depth: 2},{id: 5, depth: 1},{id: 6, depth: 2},{id: 7, depth: 2},{id: 8, depth: 1},{id: 9, depth: 2},{id: 10, depth: 3},{id: 11, depth: 3}];
const roots = createForest(flatdata);
console.log(roots);
Irregular depths
If the depth values do not correspond to the actual depth of the nodes, but leave gaps, then use a "dictionary" (a plain object) to record the mapping of the depth property values with which real depth they correspond with:
function createForest(flatdata) {
const path = [{ children: [] }];
const depthMap = { 0: 0 };
for (const obj of flatdata) {
path[(depthMap[obj.depth] ??= path.length) - 1].children.push(
path[depthMap[obj.depth]] = { ...obj, children: []}
);
}
return path[0].children;
}
// demo
const flatdata = [{id: 1, depth: 10},{id: 2, depth: 20},{id: 3, depth: 30},{id: 4, depth: 20},{id: 5, depth: 10},{id: 6, depth: 20},{id: 7, depth: 20},{id: 8, depth: 10},{id: 9, depth: 20},{id: 10, depth: 30},{id: 11, depth: 30}];
const roots = createForest(flatdata);
console.log(roots);
If however, the only irregularity is that the depth does not always start at 1, but sometimes at 2, it will be more efficient to prefix the input data with a dummy depth-one node, use the first function, and then remove the dummy "root" (with depth 1) from the result.
Go through the array and add each item to the tree as well as to a trail of breadcrumbs. Each next item either goes as a child to the last one or you backtrack through the breadcrumb trail to the correct depth where it needs to be inserted:
const peek = arr =>
arr[arr.length-1];
function toTree(arr) {
const tree = [];
const trail = [];
for (const item of arr) {
while ((peek(trail)?.depth ?? 0) >= item.depth) {
trail.pop();
}
const current = peek(trail)?.children ?? tree;
const treeNode = {...item, children: []};
current.push(treeNode);
trail.push(treeNode);
}
return tree;
}
const array = [
{id: 1, depth: 1, },
{id: 2, depth: 2, },
{id: 3, depth: 3, },
{id: 4, depth: 2, },
{id: 5, depth: 1, },
{id: 6, depth: 2, },
{id: 7, depth: 2, },
{id: 8, depth: 1, },
{id: 9, depth: 2, },
{id: 10, depth: 3 },
{id: 11, depth: 3 },
]
console.log(toTree(array));
This solution clones each item, in order to add the .children property. If no cloning is necessary, item can be directly mutated.
You could take an array of the last inserted objects.
const
data = [{ id: 1, depth: 1 }, { id: 2, depth: 2 }, { id: 3, depth: 3 }, { id: 4, depth: 2 }, { id: 5, depth: 1 }, { id: 6, depth: 2 }, { id: 7, depth: 2 }, { id: 8, depth: 1 }, { id: 9, depth: 2 }, { id: 10, depth: 3 }, { id: 11, depth: 3 }],
result = data.reduce((r, { depth, ...o }) => {
r[depth - 1].push({ ...o, children: r[depth] = [] });
return r;
}, [[]])[0];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const id = [1, 4, 10]
const data =[{id: 1, name: Banana}, {id: 2, name: Mango}, {id: 3, name: Chili}, {id: 4, name: WaterMelon}, {id: 10, name: WaterMelon}]
I tried filter It's showing me empty value. I want to remove matched by id in an array.
Use filter() with includes():
const result = data.filter(({id}) => !ids.includes(id));
Full snippet:
const ids = [1, 4, 10];
const data = [{
id: 1,
name: 'Banana'
}, {
id: 2,
name: 'Mango'
}, {
id: 3,
name: 'Chili'
}, {
id: 4,
name: 'WaterMelon'
}, {
id: 10,
name: 'WaterMelon'
}];
const result = data.filter(({id}) => !ids.includes(id));
console.log(result);
If you want to filter out matched ID, try this code:
const result = data.filter(({id}) => ids.indexOf(id) < 0);
const ids = [1, 4, 10]
const data =[{id: 1, name: 'Banana'}, {id: 2, name: 'Mango'}, {id: 3, name: 'Chili'}, {id: 4, name: 'WaterMelon'}, {id: 10, name: 'WaterMelon'}];
const result = data.filter(({id}) => ids.indexOf(id) < 0);
console.log(result);
using filter it will select the ids of data,
and your object name type data should be string or maybe you want it as a variable so you will not get error.
data.filter((object) => !id.includes(object.id))
const result=data.filter((value)=>!id.includes(value.id))
The data structure that I am trying to achieve would look as so :
I would like the list_id to become a key in a object, and hold all the id's of the items that have the matching list id.
var lists = { (list_id)1 : [1, 2, 3]
(list_id)2 : [4, 5, 6]
(list_id)3 : [7, 8, 9]
(list_id)4 : [10, 11, 12] };
this object is created from a json data structure that looks like this:
let json = [{ id: 1, list_id: 1 }, { id: 2, list_id: 1 },
{id: 3, list_id: 1 }, {id: 4, list_id: 2 },
{id: 5, list_id: 2 }, {id: 6, list_id: 2 },
{id: 7, list_id: 3 }, {id: 8, list_id: 3 },
{id: 9, list_id: 3 }, {id: 10, list_id: 4 },
{id: 11, list_id: 4 }, {id: 12, list_id: 4 }]
I can make an object that holds all the list_id's as keys but am getting stumped on pushing the actions_id into the value pair array with the matching list id.
let listAll = {};
json.forEach(function(lista, index, listb) {
listAll[lista.list_id] = [];
if ( listAll[lista.list_id] === lista.list_id){
listAll[lista.list_id].push(lista.id)
} else {
listAll[lista.list_id] = [lista.id];
}
});
My goal is to have and object that contains a key for every list_id currently avaliable from the actions.
Then add every action that contains the matching list_id into a value pair array.
the current output of this code is
{ '1': [ 3 ], '2': [ 6 ], '3': [ 9 ], '4': [ 12 ] }
which does not contain all numbers, each array should contain 3 numbers.
An alternative is using the function reduce to group the objects by a specific key = ['list_id', list_id].join('').
let json = [{ id: 1, list_id: 1 }, { id: 2, list_id: 1 }, {id: 3, list_id: 1 }, {id: 4, list_id: 2 }, {id: 5, list_id: 2 }, {id: 6, list_id: 2 }, {id: 7, list_id: 3 }, {id: 8, list_id: 3 }, {id: 9, list_id: 3 }, {id: 10, list_id: 4 }, {id: 11, list_id: 4 }, {id: 12, list_id: 4 }],
result = json.reduce((a, {id, list_id}) => {
let key = ['list_id', list_id].join(''); // For example: this is creating ['list_id', 1] to list_id1
(a[key] || (a[key] = [])).push(id);
return a;
}, Object.create(null)/*This is only to create an object without prototype -> {}*/);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Why don't you try hasOwnProperty instead?
var listAll = {};
json.forEach(function(list, index) {
if (listAll.hasOwnProperty(list.list_id)) {
listAll[list.list_id].push(list.id);
}else {
listAll[list.list_id] = [list.id];
}
});
console.log(listAll);
I have an array
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
]
I need output like this:
[{id: 1, items: [3, 4, 5]}, {id: 2, items: [6,7]}, {id: 3, items: [8]}]
Here's a solution that first groups by id and then maps across the groupings to get the required collection:
let result = _(a)
.groupBy('id')
.map( (group ,id) => ({id: id, items: _.map(group, 'item')}))
.value()
It's pretty ugly, but then other answers are not pretty either
var a = [
{id: 1, item: 3},
{id: 1, item: 4},
{id: 1, item: 5},
{id: 2, item: 6},
{id: 2, item: 7},
{id: 3, item: 8}
];
var ret = _.chain(a)
.groupBy(elt => elt.id)
.mapValues(elt => _.reduce(elt, (acc, sub) => acc.concat(sub.item),[]))
.map((value, key) => ({id: key, items:value}))
.value();
console.log(ret);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
I have two arrays of objects like this:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}]
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}]
I need to compare the elements of the two arrays by Id and remove the elements from arr1 that are not presented in arr2 ( does not have element with that Id). How can I do this ?
var res = arr1.filter(function(o) {
return arr2.some(function(o2) {
return o.Id === o2.Id;
})
});
shim, shim, shim.
You can use a function that accepts any number of arrays, and returns only the items that are present in all of them.
function compare() {
let arr = [...arguments];
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var arr3 = [{Id: 1, Name: "Test1"}, {Id: 6, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var new_arr = compare(arr1, arr2, arr3);
console.log(new_arr);
function compare() {
let arr = [...arguments]
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
Making use of a hash (a Set) will give a performance gain:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"},
{Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}];
arr1 = arr1.filter(function (el) {
return this.has(el.Id);
}, new Set(arr2.map(el => el.Id)));
console.log(arr1);
A new Set is created that gets the Id values from arr2:
"1","3"
That Set is passed as the thisArg to filter, so that within the filter callback it is available as this.