Count array with matching data then push to new array - javascript

I need to figure out how to count an array then combine only the matching ones.
example
const info = [ { name: 'John', date:'2022-04-11', type: '2', time: 5.00 },
{ name: 'Dave', date:'2022-04-12', type: '3', time: 6.00 },
{ name: 'John', date:'2022-04-11', type: '2', time: 2.00 },
{ name: 'John', date:'2022-04-15', type: '2', time: 3.00 } ];
The expected result should check for the same type, name and date them combine time.
and the new array should look something like this.
It can be done with a forloop, but I would like to try creating a solution with es6.
But I am a bit unsure how to approach this.
const expected = [ { name: 'John', date:'2022-04-11', type: '2', time: 7.00 },
name: 'Dave', date:'2022-04-12', type: '3', time: 6.00 },
name: 'John', date:'2022-04-15', type: '2', time: 3.00 } ];

You could use a combination of .reduce and .find
const info = [ { name: 'John', date:'2022-04-11', type: '2', time: 5.00 },
{ name: 'Dave', date:'2022-04-12', type: '3', time: 6.00 },
{ name: 'John', date:'2022-04-11', type: '2', time: 2.00 },
{ name: 'John', date:'2022-04-15', type: '2', time: 3.00 } ];
const result = info.reduce((acc, x) => {
const foundObj = acc.find(y => y.name === x.name && y.date === x.date && y.type === x.type);
if (foundObj) {
foundObj.time += x.time;
} else {
acc.push(x);
}
return acc;
}, []);
console.log(result)

You could for example create an object, where the key is a combination of name, date, type and the value is time
let grouped = info.reduce((acc, curr) => {
let key = `${curr.name}${curr.date}${curr.type}`;
if (!acc[key]) {
acc[key] = {
name: curr.name,
date: curr.date,
type: curr.type,
time: curr.time,
};
} else {
acc[key].time += curr.time;
}
return acc;
}, {});
let expected = Object.values(grouped);

Related

Combine 2 arrays based on their common id [duplicate]

This question already has answers here:
Merge two array of objects based on a key
(23 answers)
Closed 4 months ago.
I have 2 different arrays, that i want to combine.
The first one looks like following:
const Cats[] = [
{ id: '1', name: 'Smiley' },
{ id: '2', name: 'Purple' },
]
the second one:
const catAges[] = [
{ id: '4', age: '13', catId: '1' },
{ id: '5', age: '4', catId: '2' },
];
and i want to combine them where id from Cats[] and catId from catAges[] are the same and have a result like following:
{ id: '4', age: '13', cat: { id: '1', name: 'Smiley' } },
{ id: '5', age: '4', cat: { id: '2', name: 'Purple' } },
i get the arrays from 2 different async functions looking like this:
const cats = [await getByCatId("1"), await getByCatId("2")];
const catsAge = await getCatsAges();
But i need help in how i combine these 2 and map them. I've tried something like this but without any success:
const all = (cats, catsAge) =>
cats.map(cats=> ({
...catsAge.find((cats) => (catsAge.catId === cats.id) && catsAge),
...cats
}));
console.log(all(cats, catsAge));
Thankful for any help in how to move forward.
const Cats = [
{ id: '1', name: 'Smiley' },
{ id: '2', name: 'Purple' },
]
const catAges = [
{ id: '4', age: '13', catId: '1' },
{ id: '5', age: '4', catId: '2' },
];
const transformed = catAges.map(item => {
const cat = Cats.find(cat => cat.id === item.catId);
if (cat) {
item.cat = cat;
delete item.catId;
}
return item;
});
console.log(transformed);
The problem with your function is just that you're re-using the cats variable too much, so in your .find comparision you're comparing an element from catsAge (as cats.id) and the catsAge array (as catsAge.catId) which is undefined.
Try this:
const all = (cats, catsAge) =>
cats.map((cat) => ({
...catsAge.find((catsAge) => catsAge.catId === cat.id),
...cat,
}));
Pro tip: Learn+Use Typescript and the compiler would catch these errors for you :)
const Cats = [
{ id: '1', name: 'Smiley' },
{ id: '2', name: 'Purple' },
]
const catAges = [
{ id: '4', age: '13', catId: '1' },
{ id: '5', age: '4', catId: '2' },
];
catAges.map(catage => {
const cat = Cats.find(c => c.id == catage.catId);
if(cat) {
delete catage.catId;
catage.cat = cat;
return catage;
}
});

How do I create an array of objects with a nested array based on a similar key?

I have an array that looks something like this
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
As you can see, the organization name is something I want to key off of and create a data structure like this:
const output = [
// data.value will be their ID
{
organizationName: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' },
],
},
{
organizationName: 'Organization B',
data: [
{ label: 'Person 3', value: '3' },
],
},
]
What I've tried
I know I want to use reduce for something like this, but I feel like I'm off:
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
if (organizationName) {
acc.push({ organization: organizationName, data: [] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
}
return acc;
}, [] as any);
However the output comes out to something like this:
[
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization B', data: []},
];
data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?
Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.
const externalPeople = {
data : [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
],
};
const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
name: externalPersonName,
id
} = currentValue;
// Is the org new? Yes, create an entry for it
if (!acc[organizationName]) {
acc[organizationName] = { organization: organizationName, data: [] };
}
// push the person to the organization
acc[organizationName].data.push({ name: externalPersonName, value: id });
return acc;
}, {}));
console.log(providerOptions)
Here is another solution
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const result = example.reduce((res, entry) => {
const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
if(recordIndex >= 0) {
res[recordIndex].data.push({ label: entry.name, value: entry.id});
} else {
const record = {
organizationName: entry.organization.name,
data: [{ label: entry.name, value: entry.id }]
};
res.push(record);
}
return res;
}, []);
console.log(result);
You are not checking if the value is already present in your accumulation acc
You can check it with a simple find in the if statement since it's an array
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
//Check if organization is not present already
if (!acc.find(a => a.organization === organizationName)) {
//Add also the data of the element your are processing
acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
}
return acc;
}, [] as any);
I also added the data of the first element of the group you create when adding the organization.
The result should be as your expected output:
[
{
organization: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' }
]
},
{
organization: 'Organization B',
data: [
{ label: 'Person 3', value: '3' }
]
}
]
Hope it helps!
Compare this solution (using Lodash) with other solutions. Which one emphasises your intentions at most? This is why we use Lodash in our company - to maintain code as declarative as we can, because code readability, with minimum cognitive overload, is most important goal during coding.
const persons = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const personsByOrganizations = _.groupBy(persons, 'organization.name')
const output = _.map(personsByOrganizations, (persons, organizationName) => ({
organizationName,
data: _.map(persons, ({ name, id }) => ({
label: name,
value: id
}))
}))
Something like that with using a Set?
result = [...new Set(example.map(d => d.organization.name))].map(label => {
return {
organizationName: label,
data: example.filter(d => d.organization.name === label).map(d => {
return {label: d.name, value: d.id}
})
}
})
`

Merge objects in array of object removing duplicates

I have this array of objects:
const a = [
{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
I would like to have a result like this, so having one object for id:1 and a merged array in role property:
const a = [
{
id: 1,
name: 'John',
role: ['admin', 'user']
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
EDIT:
I am able to remove duplicates when I have just to properties in the object. In my case I don't know how to retrieve the name property using the following snippet:
const b = [...new Set(a.map(d => d.id))].map(obj => {
return {
id: obj,
data: a.filter(d => d.id === obj).map(d => d.role)
}
})
You could take an object for grouping and use an array for additional roles.
const
data = [{ id: 1, name: 'John', role: 'admin' }, { id: 1, name: 'John', role: 'user' }, { id: 2, name: 'Max', role: 'user' }],
result = Object.values(data.reduce((r, o) => {
if (!r[o.id]) r[o.id] = { ...o };
else r[o.id].role = [].concat(r[o.id].role, o.role);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It can be done very simply with a reducer:
const a = [
{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
const b = a.reduce((acc, el)=>{
const existingEl = acc.find(accEl=>accEl.id === el.id)
if(existingEl) existingEl.role.push(el.role)
// a very inelegant way of building a shallow copy with
// a bit of a data structure change
else acc.push({id: el.id, name: el.name, role:[el.role]})
return acc
}, [])
console.log(b)
give this a try
const a = [
{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
const newArr = a.reduce((acc, val) => {
const findIndex = acc.findIndex(f => f.id === val.id);
if (findIndex > -1) {
if ((typeof acc[findIndex].role === 'string')) {
acc[findIndex].role = [acc[findIndex].role, val.role]
} else {
acc[findIndex].role.push(val.role)
}
} else {
acc.push(val)
}
return acc
}, []);
console.log(newArr)
You can iterate over each item in your input, storing its data on an object keyed by the item's id property. Using a Set to collect the roles during iteration ensures that no duplicates will exist in the end result:
function mergeRoles (users) {
const merged = {};
for (const {id, name, role} of users) {
(merged[id] ??= {id, name, role: new Set([role])}).role.add(role);
}
return Object.values(merged).map(user => ({...user, role: [...user.role]}));
}
const input = [
{ id: 1, name: 'John', role: 'admin' },
{ id: 1, name: 'John', role: 'user' },
{ id: 2, name: 'Max', role: 'user' },
];
const result = mergeRoles(input);
console.log(result);
For problems like this I usually turn the array into an object dictionary to merge all the duplicates, then convert the dictionary back to an array:
const a = [{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
];
// Merge duplicates using object dictionary.
let itemsById = {};
for (let item of a) {
if (!itemsById[item.id]) {
// Id not seen yet.
item.role = [item.role];
itemsById[item.id] = item;
} else {
// Duplicate Id.
itemsById[item.id].role.push(item.role);
}
}
// Convert object dictionary back to array.
let newArray = [];
for (const id in itemsById) {
let item = itemsById[id];
if (item.role.length == 1) {
item.role = item.role[0];
}
newArray.push(item);
}
console.log(newArray);

JS merge two subarray using map

I use the following to merge two arrays:
var mySeries = [
{ name: '4', data: [4] },
{ name: '3', data: [3] }
];
var mySeries1 = [
{ name: '5', data: [0] },
{name: '4', data:[0]},
{name: '3', data:[0]},
{name: '2', data:[0]},
{ name: '1', data: [0] }
];
var res = mySeries1.map(obj => mySeries.find(o => o.name === obj.name) || obj);
console.log(res);
Works great; however, my challenge is my array is structured as:
var myArray = [{
mySeries : [
{ name: '4', data: [4] },
{ name: '3', data: [3] }],
mySeries1 : [
{ name: '5', data: [0] },
{ name: '4', data: [0] },
{ name: '3', data: [0] },
{ name: '2', data: [0] },
{ name: '1', data: [0] }]
}];
];
So I need to map subarrays, tried using the following:
var res = myArray.mySeries1.map(obj => myArray.mySeries.find(o => o.name === obj.name) || obj);
I get this error:
Cannot read properties of undefined (reading 'map')
How can I point to the subarray?
var myArray = [{
mySeries : [
{ name: '4', data: [4] },
{ name: '3', data: [3] }],
mySeries1 : [
{ name: '5', data: [0] },
{ name: '4', data: [0] },
{ name: '3', data: [0] },
{ name: '2', data: [0] },
{ name: '1', data: [0] }
]
}];
var res = myArray[0].mySeries1.map(obj => myArray[0].mySeries.find(o => o.name === obj.name) || obj);
console.log(res)
you could change your array to dictionary and iterate it like you already did.
var myArray = {
mySeries : [
{ name: '4', data: [4] },
{ name: '3', data: [3] }],
mySeries1 : [
{ name: '5', data: [0] },
{ name: '4', data: [0] },
{ name: '3', data: [0] },
{ name: '2', data: [0] },
{ name: '1', data: [0] }]
};
i.e. var res = myArray.mySeries1.map(obj => myArray.mySeries.find(o => o.name === obj.name) || obj);

Filter two array of objects and get a resultant array

I'm trying to check for condition where carId in one array is equal to id of another array.
Below is the code snippet.
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const result = arr2.map(val => arr2.find(val.carId === id))
console.log(result)
The result that I'm expecting is
[{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
Could anyone please help?
While you should use .filter() on arr1, and pass a callback to .find(), I'd probably first convert arr2 to a simple list of IDs and use .includes() instead.
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const ids = arr2.map(o => o.carId);
const result = arr1.filter(val => ids.includes(val.id))
console.log(result)
or better yet, convert arr2 to a Set.
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const ids = arr2.map(o => o.carId);
const idSet = new Set(ids);
const result = arr1.filter(val => idSet.has(val.id))
console.log(result)
const arr1 = [{
id: '1',
type: 'car',
name: 'BMW',
},
{
id: '2',
type: 'car',
name: 'Audi',
},
{
id: '3',
type: 'car',
name: 'Benz',
}
];
const arr2 = [{
carId: '1'
}, {
carId: '3'
}];
const result = arr1.filter(a1val => arr2.find(a2val => a2val.carId === a1val.id) !== undefined);
console.log(result);
This might work
const result = arr2.map(val => arr1.find(item => item.id === val.carId))

Categories