combining similar key/value pair into an array - javascript

I am wondering if I can combine multiple objects based on a given id into an array of an object? Hopefully I can explain this better below:
trying to have an output of each index item of carCollection to be like this:
[
{
brand: 'Porsche',
model: ['Cayenne', 'Macan'],
id: 1
},
{
brand: 'BMW',
model: ['M4','M3'],
id: 3
}
]
But the code below duplicates id:1 car brand Porsche into the carCollection:
[
{
brand: 'Porsche',
model: ['Cayenne'],
id: 1,
},
{
brand: 'Porsche',
model: ['Macan'],
id: 1
}
]
let cars = {
brands: [ {name:'Porsche', id:1}, {name:'Mercedes-Benz', id:2},{name:'BMW', id:3},],
models: [ {name:'Cayenne', id:1}, {name:'C45', id:2}, {name:'M4', id:3}, {name:'M3', id:3}, {name:'Macan', id:1}]
}
// empty array
let carCollection = []
let { brands, models } = cars;
function carMatcher(brands, models){
for(let brand of brands){
for(let model of models){
const carObject = {
brand: '',
model: [],
id: 0
}
if(model.id === brand.id){
carObject.brand = brand.name
carObject.model.push(model.name)
carObject.id = brand.id
carCollection.push(carObject)
}
}
}
}
carMatcher(brands, models)

You can easily achieve this result using Map and forEach
let cars = {
brands: [
{ name: "Porsche", id: 1 },
{ name: "Mercedes-Benz", id: 2 },
{ name: "BMW", id: 3 },
],
models: [
{ name: "Cayenne", id: 1 },
{ name: "C45", id: 2 },
{ name: "M4", id: 3 },
{ name: "M3", id: 3 },
{ name: "Macan", id: 1 },
],
};
const dict = new Map();
cars.brands.forEach(({ name, id }) => {
dict.set(id, { brand: name, id, model: [] });
});
cars.models.forEach(({ name, id }) => {
if (dict.has(id)) dict.get(id).model.push(name);
});
const result = [...dict.values()];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
EDIT: Simpler version
let cars = {
brands: [
{ name: "Porsche", id: 1 },
{ name: "Mercedes-Benz", id: 2 },
{ name: "BMW", id: 3 },
],
models: [
{ name: "Cayenne", id: 1 },
{ name: "C45", id: 2 },
{ name: "M4", id: 3 },
{ name: "M3", id: 3 },
{ name: "Macan", id: 1 },
],
};
let dict = {},
result = [];
for (let val of cars.brands) {
dict[val.id] = {
brand: val.name,
id: val.id,
model: [],
};
}
for (let val of cars.models) {
const id = val.id;
const name = val.name;
const objInDict = dict[id];
if (objInDict) {
objInDict.model.push(name);
}
}
for (let key in dict) {
result.push(dict[key]);
}
console.log(result);
Simplest
let cars = {
brands: [
{ name: "Porsche", id: 1 },
{ name: "Mercedes-Benz", id: 2 },
{ name: "BMW", id: 3 },
],
models: [
{ name: "Cayenne", id: 1 },
{ name: "C45", id: 2 },
{ name: "M4", id: 3 },
{ name: "M3", id: 3 },
{ name: "Macan", id: 1 },
],
};
let result = [];
const { brands, models } = cars;
for (let brand of brands) {
const { name, id } = brand;
const newObj = { brand: name, id, model: [] };
for (let model of models) {
const { name, id } = model;
if (newObj.id === id) {
newObj.model.push(name);
}
}
result.push(newObj);
}
console.log(result);

Related

How to loop through two arrays of objects and get a new array with some data modified?

How to loop through two arrays of objects and get a new array with some data modified?
Arrays:
const products = [
{
brand: 'Levis',
category: 'Jeans',
},
{
brand: 'Levis',
category: 'Jeans',
},
{
brand: 'Levis',
category: 'Tees',
},
];
const categories = [
{
name: 'Jeans',
},
{
name: 'Tees',
},
];
Need new categories array like this with new prop productCount:
const newCategories = [
{
name: 'Jeans',
productCount: 2,
},
{
name: 'Tees',
productCount: 0,
},
];
I tried this way but it doesn't work:
const newArr = categories.map((category) => {
let count = 0;
const index = products.findIndex((product) => category.name === product.category);
if (index > -1) {
return {
...category,
productCount: count++,
};
}
return {
...category,
productCount: 0,
};
});
Increasing the count number will not in that case because it will always start with zero. Instead, you can use the filter() method to find the number of products with a specific category and assign this number to productCount attribute.
const products = [{
brand: 'Levis',
category: 'Jeans',
},
{
brand: 'Levis',
category: 'Jeans',
},
{
brand: 'Levis',
category: 'Tees',
},
];
const categories = [{
name: 'Jeans',
},
{
name: 'Tees',
},
];
const newArr = categories.map((category) => {
const numberOfItems = products.filter((product) => category.name === product.category);
return {
...category,
productCount: numberOfItems.length,
};
});
console.log(newArr)
You can create an object and the transform it to array, something like this:
const products = [
{
brand: "Levis",
category: "Jeans"
},
{
brand: "Levis",
category: "Jeans"
},
{
brand: "Levis",
category: "Tees"
}
];
const categoriesObj = {};
products.forEach(({ brand, category }) => {
categoriesObj[category] ??= {
name: category,
productCount: 0
};
++categoriesObj[category].productCount;
});
const newCategories = Object.values(categoriesObj);
console.log(newCategories);
You can use the Array#Map method and add a productCount property using the Array#filter method
const products = [{
brand: 'Levis',
category: 'Jeans',
},
{
brand: 'Levis',
category: 'Jeans',
},
{
brand: 'Levis',
category: 'Tees',
},
];
const categories = [{
name: 'Jeans',
},
{
name: 'Tees',
},
];
const newCategories = [...categories].map(category => ({
...category,
productCount: products.filter(product => product.category === category.name).length
}))
console.log(newCategories)
You could do this with Array.reduce(), incrementing the productCount for each item. This should also be efficient, requiring only one iteration of the products array.
We'd run the reduce over both arrays, ensuring that we'll end up with a productCount of zero where no products for that category exist.
const products = [ { brand: 'Levis', category: 'Jeans', }, { brand: 'Levis', category: 'Jeans', }, { brand: 'Levis', category: 'Tees', }, ];
const categories = [ { name: 'Jeans', }, { name: 'Tees', }, { name: 'Foo', } ];
const result = Object.values([...categories, ...products].reduce((acc, { brand, category, name }) => {
const key = name || category;
acc[key] = acc[key] || { name: key, productCount: 0 };
if (category) acc[key].productCount++;
return acc;
}, {}));
console.log('Result:', result);
.as-console-wrapper { max-height: 100% !important; }

fillter arrays of objects

i have two arrays.
const department = [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
];
const models = [
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }],
},
{
id: '54',
name: 'model2',
departments: [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
],
},
];
i need to render accordions with department names and accordion details with matching models names. My question is how to filter those arrays to get models
We can map through the departments array, and add a models property that equals the models array, but filtered only to the ones that contain a matching department id.
const departments = [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
];
const models = [
{
id: "23",
name: "model1",
departments: [{ id: "1", name: "department1" }],
},
{
id: "54",
name: "model2",
departments: [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
],
},
];
const getDepartmentsWithModels = () => {
return departments.map((department) => {
return {
...department,
models: models.filter((model) => {
const modelDepartmentIds = model.departments.map(({ id }) => id);
return modelDepartmentIds.includes(department.id);
}),
};
});
};
console.log(getDepartmentsWithModels());
// [ { id: '1', name: 'department1', models: [ [Object], [Object] ] },
// { id: '2', name: 'department2', models: [ [Object] ] } ]```
I've built some code, which iterates over the departments. For each department it iterates the models and for each model it checks if the department is within the model departments.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }]
},
{
id: '54',
name: 'model2',
departments: [{ id: '1', name: 'department1' },{ id: '2', name: 'department2' }]
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.forEach(model => {
if (model.departments.find(modelDep => dep.id===modelDep.id)) {
console.log(` Model: ${model.name}`)
}
})
})
If you could change your data objects, then your code could be much smoother.
I've changed your data objects slightly by just reducing the departments in a model to be an array of department id's. This code iterates over the departments. For each department it filters the models and iterates over the filtered models to output them to the console. This is lesser code and provides much better performance.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: ['1']
},
{
id: '54',
name: 'model2',
departments: ['1', '2']
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.filter(model => model.departments.includes(dep.id)).forEach(model => {
console.log(` Model: ${model.name}`)
})
})
There are two solutions.
Using Array.reduce() --> returns an object where the key is department name and value is an array of the names of matching models:
let data1 = models.reduce((res, curr) => {
curr.departments.forEach(dep => {
if (!res[dep.name]) {
res[dep.name] = [curr.name]
} else {
if (!res[dep.name].includes(curr.name)) {
res[dep.name].push(curr.name);
}
}
})
return res;
}, {});
Using map and filter --> returns an array of kind:
[{department: [names of the models]},...]
let data2 = department.map(dep => {
let matchingModels = models.filter(model => {
return model.departments.filter(modDep => {
return modDep.name === dep.name;
}).length > 0;
}).map(mod => {
return mod.name;
});
return {
department: dep.name,
models: matchingModels
}
});

Nesting then grouping objects into arrays

I'm attempting to convert an array that I get in this format:
data = [
{ name: 'Buttons/Large/Primary', id: '1:23' },
{ name: 'Buttons/Large/Secondary', id: '1:24' },
{ name: 'Buttons/Medium/Primary', id: '1:25' },
{ name: 'Buttons/Medium/Secondary', id: '1:26' },
{ name: 'Forms/Text', id: '2:1' },
{ name: 'Forms/Checkbox', id: '2:2' },
];
to an array in this format:
data = [
{
name: "Buttons",
id: '1:23',
components: [{
name: "Large",
id: '1:23',
components: [{
name: "Primary",
id: '1:23'
}, {
name: "Secondary",
id: '1:24'
}]
},{
name: "Medium",
id: '1:25',
components: [{
name: "Primary",
id: '1:25'
}, {
name: "Secondary",
id: '1:26'
}]
}]
}, {
name: "Forms",
id: '2:1',
components: [{
name: "Text",
id: '2:1'
},{
name: "Checkbox",
id: '2:2'
}]
}
];
My approach was to create arrays from each object in the original dataset by splitting the name property at '/', then nest them inside each other. This is what I have so far, which nests each item in the original array, but lacks grouping them together like my target format shows. Suggestions?
function nestItems(obj, path, value) {
let component = {};
let temp = component;
for (let i = 0; i < path.length; i++) {
let component = temp;
component.name = path[i];
component.id = value;
if (path.length - 1 === i) {
} else {
component.components = {};
temp = component.components;
}
}
obj.push(component)
}
let obj = [];
for (let i = 0; i < data.length; i++) {
let path = data[i].name.split('/');
nestItems(obj, path, data[i].id);
}
console.log(obj)
I agree with your approach for splitting with /.
Here's my approach for using reduce to create a map and generating the final array:
const data = [
{ name: 'Buttons/Large/Primary', id: '1:23' },
{ name: 'Buttons/Large/Secondary', id: '1:24' },
{ name: 'Buttons/Medium/Primary', id: '1:25' },
{ name: 'Buttons/Medium/Secondary', id: '1:26' },
{ name: 'Forms/Text', id: '2:1' },
{ name: 'Forms/Checkbox', id: '2:2' },
];
const map = data.reduce((acc, curr) => {
const { id } = curr;
const [parent, sub, subSub] = curr.name.split('/');
if (acc[parent]) {
if (acc[parent][sub]) {
acc[parent][sub][subSub] = { id };
} else {
acc[parent][sub] = { id };
if (subSub) {
acc[parent][sub][subSub] = { id };
}
}
} else {
acc[parent] = { id };
if (sub && subSub) {
acc[parent][sub] = {
id,
[subSub]: { id }
};
} else if (sub) {
acc[parent][sub] = { id };
};
}
return acc;
}, {});
const result = Object.keys(map).map(parentName => {
const { id: parentId, ...subs } = map[parentName];
const parentObj = { name: parentName, id: parentId };
parentObj.components = Object.keys(subs).map(subName => {
const { id: subId, ...subSubs } = subs[subName];
const subObj = { name: subName, id: subId };
if (Object.keys(subSubs).length) {
subObj.components = Object.keys(subSubs).map(subSubName => ({ name: subSubName, id: subSubs[subSubName].id }));
}
return subObj;
});
return parentObj;
});
console.log(result);

Search for an object in a recursive object structure (representing a file system)

I'd like to pass as an input the name of a folder that I want to search for, and get as an output the object that it belongs to.
My array is like this:
const array = {
item: [{
name: "parentFolder1",
item: [{
name: "subFolder1",
item: []
},
{
name: "subFolder2",
item: []
}
]
},
{
name: "parentFolder2",
item: [{
name: "sub1",
item: []
},
{
name: "sub2",
item: []
}
]
}
]
};
const sub = Object.values(array).map(x =>
x.find(y => y.item.find(obj => obj.name = "sub2")))
console.dir(sub)
The output I want:
{
name: "sub2",
item: []
}
The output I get:
[ { name: 'parentFolder1', item: [ [Object], [Object] ] } ]
const array = {
item: [{
name: "parentFolder1",
item: [{
name: "subFolder1",
item: []
},
{
name: "subFolder2",
item: []
}
]
},
{
name: "parentFolder2",
item: [{
name: "sub1",
item: []
},
{
name: "sub2",
item: []
}
]
}
]
};
const val = array.item.map(folder => folder.item.find(obj => obj.name === "sub2")).find(val => val)
console.log(val)
If you need it to be recursive
function find(name, obj) {
if(obj.name === name) return obj;
return obj.item.reduce((result, item) => result || find(name, item), false)
}
const array = {
item: [{
name: "parentFolder1",
item: [{
name: "subFolder1",
item: []
},
{
name: "subFolder2",
item: [{
name: "subsub",
item: [{
name: "searchme",
item: []
}]
}]
}
]
},
{
name: "parentFolder2",
item: [{
name: "sub1",
item: []
},
{
name: "sub2",
item: []
}
]
}
]
};
console.log(find("searchme", array))

Javascript filtering nested arrays

I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}

Categories

Resources