Let's say I have a collection of documents. To make this simpler, let's say all these documents have just 2 fields, name and id, like below.
I want to get the document with the greatest value of "id". Can I use the .find({}) with some parameters? If so, I can't figure out which parameter would find me the max value of a certain field.
[
{
name: "name1",
id: 1
}
{
name: "name2",
id: 2
}
{
name: "name1",
id: 3
}
{
name: "name1",
id: 0
}
]
let documents = [
{
name: "name1",
id: 1
},
{
name: "name2",
id: 2
},
{
name: "name1",
id: 3
},
{
name: "name1",
id: 0
}
];
let max = documents.sort( (a, b) => a.id > b.id ? -1 : 1)[0]
console.log( max );
How about
let maxIdDocument = documents.sort( (a, b) => a.id > b.id ? 1 : -1)[0]
First, sort in descending order, then get the first one:
const arr = [{
name: "name1",
id: 1
},
{
name: "name2",
id: 2
},
{
name: "name1",
id: 3
},
{
name: "name1",
id: 0
}
];
const [greatest] = arr.sort(({ id: a }, { id: b }) => b - a);
console.log(greatest);
You can sort that array and with the function pop get the object with the greatest id.
arr.slice() // This is to avoid a mutation on the original array.
let arr = [ { name: "name1", id: 1 }, { name: "name2", id: 2 }, { name: "name1", id: 3 } , { name: "name1", id: 0 } ],
greatest = arr.slice().sort(({id: a}, {id: b}) => a - b).pop();
console.log(greatest);
.as-console-wrapper { min-height: 100%; }
Related
I am new to react. I have got an issue updating a quantity.
const[persons,setPersons] = useState(personLists)
if you console.log(persons) it will give output as below. Now I want to
update qty for particular misc index of particular person index .
I have two persons in below array and each person have further two misc array
[
{
id: 1,
name: "john",
gender: "m",
misc: [
{
id: 1,
name: "xxx",
qty: 1
},
{
id: 2,
name: "xxx1",
qty: 1
}
]
},
{
id: 2,
name: "mary",
gender: "f",
misc: [
{
id: 1,
name: "aaa",
qty: 1
},
{
id: 2,
name: "bbb",
qty: 1
}
]
},
]
Now i want to update qty under misc array for that particular person.I have a function that takes the index of person array and index of misc array as below.
const updatePersonMiscQty = (personIndex, miscIndex) => {
setPersons(persons =>
persons.map((person,key) => {
const found = person.misc.find(d => key === miscIndex);
if (found) {
found.qty += 1;
}
return person;
})
}
let say my personIndex is 0 and miscIndex= is 1
so when usse click the button It should look into first person array, go to second index of misc and update qty.
I am looking for a solutions
access the items by indexes and update
setPersons(persons => {
const miscItem = persons[personIndex]?.misc?.[miscIndex]
if (miscItem ) {
miscItem.qty += 1;
}
return [...persons];
}
})
I have 2 array that consist object :
a =
[
{
id: 105647,
name: 'name1'
},
{
id: 105941,
name: 'name2'
},
{
id: 106177,
name: 'name3'
}
]
b =
[
[
{
id: 105647,
transactionDate: '2022-03-21',
order: 1
},
{
id: 105647,
transactionDate: '2022-03-22',
order: 2
}
],
[
{
id: 106177,
transactionDate: '2022-03-24',
order: 1
},
]
]
As you can see, that in b is not always have data for each id in variable a.
And my desired output would be
a =
[
{
id: 105647,
name: 'name1',
dataB:
[
{
id: 105647,
transactionDate: '2022-03-21',
order: 1
},
{
id: 105647,
transactionDate: '2022-03-22',
order: 2
}
]
},
{
id: 105941,
name: 'name2'
},
{
id: 106177,
name: 'name3',
dataB:
[
{
id: 106177,
transactionDate: '2022-03-24',
order: 1
},
]
}
]
the b variable should be pushed to new variable dataB inside object of array on variable a. And if no dataB exist on a it would not create dataB on a / create the dataB but with empty array.
I was already try with some
for (let i = 0; i<a.length; i++) {
a[i]['dataB'] = b[i]
}
the result is not pushed by id, but by index. The problem is b not always have data for each id in variable a. And what i'm trying was not what i looking for.
Please ask me if you need more information if it's still not enough to solve that case.
If your goal is to modify a, as opposed to creating a new array, you can use Array#forEach and Array#filter methods as follows:
const a = [ { id: 105647, name: 'name1' }, { id: 105941, name: 'name2' }, { id: 106177, name: 'name3' } ];
const b = [ [ { id: 105647, transactionDate: '2022-03-21', order: 1 }, { id: 105647, transactionDate: '2022-03-22', order: 2 } ], [ { id: 106177, transactionDate: '2022-03-24', order: 1 }, ] ];
a.forEach((elA,i) => {
const others = b.filter(elB => elB.every(o => o.id === elA.id)).flat();
a[i] = others.length ? {...elA, dataB: others} : elA;
});
console.log( a );
Since in b the items are already grouped by id, Array#find may be a better method, and will not need the use of Array#flat:
const a = [ { id: 105647, name: 'name1' }, { id: 105941, name: 'name2' }, { id: 106177, name: 'name3' } ];
const b = [ [ { id: 105647, transactionDate: '2022-03-21', order: 1 }, { id: 105647, transactionDate: '2022-03-22', order: 2 } ], [ { id: 106177, transactionDate: '2022-03-24', order: 1 }, ] ];
a.forEach((elA,i) => {
const others = b.find(elB => elB.every(o => o.id === elA.id));
a[i] = others ? {...elA, dataB: others} : elA;
/*OR: you can replace the above one line with the following 3
if( others ) {
a[i].dataB = others;
}*/
});
console.log( a );
loop thought a array,
on each iteration find all matches
if array is not empty - assign it to new field dataB
const a =
[
{
id: 105647,
name: 'name1'
},
{
id: 105941,
name: 'name2'
},
{
id: 106177,
name: 'name3'
}
]
const b =
[
[
{
id: 105647,
transactionDate: '2022-03-21',
order: 1
},
{
id: 105647,
transactionDate: '2022-03-22',
order: 2
}
],
[
{
id: 106177,
transactionDate: '2022-03-24',
order: 1
},
]
];
const result = a.map(i => {
const foundTrancactions = b.flat().filter(el => el.id === i.id);
return {...i, ...(foundTrancactions?.length ? {dataB: foundTrancactions} : {})}
})
console.log(result)
UPDATE
have changed b array
added flat() for b
This solution is non-mutating and employs the idea shared by Barmar in comments above.
Code Snippet
const getTransformedArray = (orig, delta) => {
// first, construct a key-value pair using "b" array (called "delta" here)
const deltaMap = delta
.flat() // since "b" is nested, use ".flat()" to flatten it
.reduce(
(acc, itm) => ({
...acc, // retain existing results from "acc" (accumulator)
[itm.id]: (
acc[itm.id] || [] // if "itm.id" already exists, keep its array
).concat([{...itm}]) // append the current "itm"
}),
{} // "acc" is initially set to empty object "{}"
);
// now, iterate through "a" array (named "orig" here)
return orig.map(
({id, name}) => ({ // de-structure to directly access "id", "name" props
id, name, // retain "id", "name"
...( // add new prop "dataB" only if it's in deltaMap
id in deltaMap
? { dataB: deltaMap[id] }
: {} // if no matching "id" in "deltaMap", don't add "dataB"
)
})
);
};
const a =
[
{
id: 105647,
name: 'name1'
},
{
id: 105941,
name: 'name2'
},
{
id: 106177,
name: 'name3'
}
]
const b = [
[
{
id: 105647,
transactionDate: '2022-03-21',
order: 1
},
{
id: 105647,
transactionDate: '2022-03-22',
order: 2
}
],
[
{
id: 106177,
transactionDate: '2022-03-24',
order: 1
},
]
];
// invoke the transform function
console.log('transformed array: ', getTransformedArray(a, b));
// display "a" and "b" arrays to show those were not mutated
console.log('original a: ', a);
console.log('original b: ', b);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments provided in the snippet above.
This question already has answers here:
group array of objects by id
(8 answers)
Closed 1 year ago.
I want to group the array of objects based on the key and concat all the grouped objects into a single array. GroupBy based on the id
example,
payload
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
expected response
[
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
}
],
[
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
]
All the matched elements are in the same array and all the arrays should be in a single array.
Array.redue will help
const input = [
{ id: 1, name: 'a' },
{ id: 1, name: 'b' },
{ id: 1, name: 'c' },
{ id: 2, name: 'b' },
{ id: 2, name: 'c' }
];
const output = input.reduce((acc, curr) => {
const node = acc.find(item => item.find(x => x.id === curr.id));
node ? node.push(curr) : acc.push([curr]);
return acc;
}, []);
console.log(output)
Extract the ids using Set so you have a unique set of them,
then loop over those ids and filter the original array based on it.
let objects = [
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
let ids = [...new Set(objects.map(i => i.id))]
let result = ids.map(id => objects.filter(n => id === n.id))
console.log(result)
you can create a object with ids array by using Array.reduce method, and get the object values by Object.values
var s = [{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
];
//go through the input array and create a object with id's, group the values to gather
var ids = s.reduce((a, c) => {
//check object has the `id` property, if not create a property and assign empty array
if (!a[c.id])
a[c.id] = [];
//push the value into desidred object property
a[c.id].push(c)
//return the accumulator
return a;
}, {});
//get the grouped array as values
var outPut = Object.values(ids);
console.log(outPut);
1) You can easily achieve the result using Map and forEach easily
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const map = new Map();
arr.forEach((o) => !map.has(o.id) ? map.set(o.id, [o]) : map.get(o.id).push(o));
const result = [...map.values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
2) You can also achieve the result using reduce
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const result = [...arr.reduce((map, curr) => {
!map.has(curr.id) ? map.set(curr.id, [curr]) : map.get(curr.id).push(curr);
return map;
}, new Map()).values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
I wonder how I can group this array based on the prefix text in name key (split the name key at the : colon) using Lodash.
const tags = [
{ name: 'Animals: Frogs', id: 1 },
{ name: 'Animals: Lions', id: 2 },
{ name: 'Birds: Crows', id: 3 }
];
to
const tags = [{
animals: [
{ name: 'Frogs', id: 1 },
{ name: 'Lions', id: 2 },
],
birds: [
{ name: 'Crows', id: 3}
]
}];
Does Lodash have any functions to handle this, or is a custom function/regex needed?
If the pure JS suffices, it can be done this way (the result is an object here, not an array, but this can be changed if needed):
const tags = [
{ name: 'Animals: Frogs', id: 1 },
{ name: 'Animals: Lions', id: 2 },
{ name: 'Birds: Crows', id: 3 }
];
const tags2 = tags.reduce(
(acc, { name, id }) => {
let [group, type] = name.split(': ');
group = group.toLowerCase();
acc[group] ??= [];
acc[group].push({ name: type, id });
return acc;
},
{},
);
console.log(tags2);
I have an array containing several objects similar to the following:
{person: {name: "Steve", id: 1}, role: 1}
{person: {name: "Phil", id: 2}, role: 1}
{person: {name: "Steve", id: 1}, role: 3}
{person: {name: "Phil", id: 2}, role: 6}
My intention is to return an array of the same type, but I'd like to return only one object per "person" with their highest role.
I understand the following will give me a single object with the highest role.
array.reduce((prev, cur) => prev.role > cur.role ? prev : cur);
How do I return each unique person and their corresponding highest role as a new array?
Like so:
{person: {name: "Steve", id: 1}, role: 3}
{person: {name: "Phil", id: 2}, role: 6}
You need to collect the objects and if you have already one with the same id check the role and take the greater one.
var data = [{ person: { name: "Steve", id: 1 }, role: 1 }, { person: { name: "Phil", id: 2 }, role: 1 }, { person: { name: "Steve", id: 1 }, role: 3 }, { person: { name: "Phil", id: 2 }, role: 6 }],
grouped = data.reduce((r, o) => {
var index = r.findIndex(({ person: { id } }) => id === o.person.id);
if (index === -1) {
r.push(o);
return r;
}
if (r[index].role < o.role) {
r[index] = o;
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Group the items by person.id with Array.reduce(), but for each id store only the object with the highest role. Convert back to array using Object.values():
const data = [{ person: { name: "Steve", id: 1 }, role: 1 }, { person: { name: "Phil", id: 2 }, role: 1 }, { person: { name: "Steve", id: 1 }, role: 3 }, { person: { name: "Phil", id: 2 }, role: 6 }];
const result = Object.values(data.reduce((r, o) => {
const id = o.person.id;
if(!r[id] || r[id].role < o.role) r[id] = o;
return r;
}, []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use Array.prototype.reduce and build out an object literal like below. Use the person.name property value as the object key and then just update the role value if you find a higher value:
var people = [{
person: {
name: "Steve",
id: 1
},
role: 1
}, {
person: {
name: "Phil",
id: 2
},
role: 1
}, {
person: {
name: "Steve",
id: 1
},
role: 3
}, {
person: {
name: "Phil",
id: 2
},
role: 6
}];
var highRoleByPerson = people.reduce((accum, el) => {
if (accum[el.person.name]) {
if (el.role > accum[el.person.name].role) {
accum[el.person.name].role = el.role;
}
} else {
accum[el.person.name] = {
person: {
name: el.person.name,
id: el.person.id
},
role: 0
};
}
return accum;
}, {});
console.log(highRoleByPerson);