how to create dynamic multi-dimensional array of objects with in javascript - javascript

I have an array of objects that has information about cars. I want to grouping on categoryId
var car = [
{ category: 1, model: "bmw" },
{ category: 1, model: "benz" },
{ category: 1, model: 'ford' }
{ category: 2, model: "kia" },
{ category: 2, model: "fiat" },
{ category: 3, model: "mg" },
];
I want this result
[
[
{ category: 1, model: 'bmw' },
{ category: 1, model: 'benz' },
{ category: 1, model: 'ford' }
],
[
{ category: 2, model: 'kia' },
{ category: 2, model: 'fiat' }
],
[
{ category: 3, model: 'mg' }
]
]
this is my solution but I want a way for this result based on reduce or ... I dont want to use if in forEach
let groupedCars = [];
cars.forEach((car) => {
if (!groupedCars[car.category]) {
groupedCars[car.category] = [];
}
groupedCars[car.category].push(car);
});

Solution using Array.prototype.reduce() method. Traverse the array and group it by category using reduce and at last, use Object.values() to get the result.
const car = [
{ category: 1, model: 'bmw' },
{ category: 1, model: 'benz' },
{ category: 1, model: 'ford' },
{ category: 2, model: 'kia' },
{ category: 2, model: 'fiat' },
{ category: 3, model: 'mg' },
];
const ret = Object.values(
car.reduce((prev, c) => {
const p = prev;
const key = c.category;
p[key] = p[key] ?? [];
p[key].push({ ...c });
return p;
}, {})
);
console.log(ret);

Not clear what you mean by result based on ES6. But, you could try using reduce method.
NOTE: Javascript arrays start with index 0 so, you might need to add some logic in your code to fix that

Related

Group array of objects by multiple nested values

I have an array of objects which presents tasks. These tasks are categorized (primary / secondary category).
let tasks = [
{
id: 1,
name: 'Cleanup desk',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 2,
name: 'Surfaces'
}
},
{
id: 2,
name: 'Cleanup office floors',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 3,
name: 'Ground'
}
},
{
id: 3,
name: 'Water plants',
primary_category: {
id: 2,
name: 'Outdoor'
},
secondary_category: {
id: 3,
name: 'Irrigation'
}
}
];
I now try to create a categories accordion in my frontend and therefore need to group my array differently. The structure should look like:
1) primary category
> secondary category
> tasks
> secondary category
> tasks
2) primary category
> secondary category
> tasks
Therefore I'm trying to achieve a structure similar to this:
let tasks_categorized = [
{
id: 1,
name: 'Indoor',
secondary_categories: [
{
id: 2,
name: 'Surfaces',
tasks: [
{
id: 1,
name: 'Cleanup desk'
}
]
},
{
id: 3,
name: 'Ground',
tasks: [
{
id: 2,
name: 'Cleanup office floors'
}
]
}
]
},
{
id: 2,
name: 'Outdoor',
secondary_categories: [
{
id: 3,
name: 'Irrigation',
tasks: [
{
id: 3,
name: 'Water plants'
}
]
}
]
}
];
I tried using groupBy by lodash but this does not allow grouping by multiple nested key-value pairs. Does anybody know an approach to solve this?
Thank you in advance!
The following provided approach is going to achieve the expected result within a single reduce cycle without any further nested loops.
It does so by implementing a reducer function which creates and/or aggregates at time a prioritized category task while iterating another task array. But most importantly it keeps track of a task item's related primary and secondary categories via a Map based lookup. This lookup reference together with a result array are properties of this function's return value which has to be partly provided as the reduce method's initial value as follows ... { result: [] }.
function createAndAggregatePrioritizedCategoryTask(
{ lookup = new Map, result }, item
) {
const { primary_category, secondary_category, ...taskRest } = item;
const { id: primaryId, name: primaryName } = primary_category;
const { id: secondaryId, name: secondaryName } = secondary_category;
const primaryKey = [primaryId, primaryName].join('###');
const secondaryKey = [primaryKey, secondaryId, secondaryName].join('###');
let primaryCategory = lookup.get(primaryKey);
if (!primaryCategory) {
// create new primary category item.
primaryCategory = {
id: primaryId,
name: primaryName,
secondary_categories: [],
};
// store newly created primary category reference in `lookup`.
lookup.set(primaryKey, primaryCategory);
// push newly created primary category reference to `result`.
result.push(primaryCategory);
}
let secondaryCategory = lookup.get(secondaryKey);
if (!secondaryCategory) {
// create new secondary category item.
secondaryCategory = {
id: secondaryId,
name: secondaryName,
tasks: [],
};
// store newly created secondary category reference in `lookup`.
lookup.set(secondaryKey, secondaryCategory);
// push newly created secondary category reference into the
// `secondary_categories` array of its related primary category.
primaryCategory
.secondary_categories
.push(secondaryCategory);
}
// push the currently processed task-item's rest-data as
// item into the related secondary category's `task` array.
secondaryCategory
.tasks
.push(taskRest);
return { lookup, result };
}
let tasks = [{
id: 1,
name: 'Cleanup desk',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 2, name: 'Surfaces' },
}, {
id: 2,
name: 'Cleanup office floors',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 3, name: 'Ground' },
}, {
id: 3,
name: 'Water plants',
primary_category: { id: 2, name: 'Outdoor' },
secondary_category: { id: 3, name: 'Irrigation' },
}];
const { result: tasks_categorized } = tasks
.reduce(createAndAggregatePrioritizedCategoryTask, { result: [] });
console.log({ tasks_categorized });
.as-console-wrapper { min-height: 100%!important; top: 0; }
You could take a dynamic approach with an array of arrays with functions and keys for the nested arrays.
const
tasks = [{ id: 1, name: 'Cleanup desk', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 2, name: 'Surfaces' } }, { id: 2, name: 'Cleanup office floors', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 3, name: 'Ground' } }, { id: 3, name: 'Water plants', primary_category: { id: 2, name: 'Outdoor' }, secondary_category: { id: 3, name: 'Irrigation' } }],
groups = [
[o => o, 'primary category'],
[o => o.primary_category, 'secondary category'],
[o => o.secondary_category, 'tasks']
],
result = tasks.reduce((r, o) => {
groups.reduce((parent, [fn, children]) => {
const { id, name } = fn(o);
let item = (parent[children] ??= []).find(q => q.id === id)
if (!item) parent[children].push(item = { id, name });
return item;
}, r);
return r;
}, {})[groups[0][1]];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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
}
});

combining similar key/value pair into an array

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);

ReactJS - Swap out all the values in an array of objects in state with another array

I am trying to swap out all the values of an array of objects in state with a whole new array of objects. However, nothing seems to be working. I've tried the following:
const list1 = [
{ id: 1, name: 'item1' },
{ id: 2, name: 'item1' },
{ id: 3, name: 'item1' },
{ id: 4, name: 'item1' },
]
const list2 = [
{ id: 1, name: 'newItem1' },
{ id: 2, name: 'newItem2' },
{ id: 3, name: 'newItem3' },
{ id: 4, name: 'newItem4' },
]
class FindTab extends Component {
state = {
status: 'loading',
location: null,
view: this.props.view,
map: this.props.map,
locationValues: list1,
}
}
this.setState(prevState => ({
locationValues: [ ...prevState.locationValues, list2 ],
}))
or just simpler:
this.setState(locationValues: list2)
Neither seem to work. Is there any guidance as to how one should replace an array of objects with another array for a state property?
You could spread the array in a new one like:
const locationValues = [ ...state.locationValues, ...list2 ]
this.setState({ locationValues })

Finding Index # from Array in JavaScript

I would like to loop through an array of elements and find the index # of the one that matches certain criteria. Take the following array:
services: [
{ _id: <ObjectId>,
name: "initiating"
},
{ _id: <ObjectId>,
name: "evaluating"
},
{ _id: <ObjectId>,
name: "servicing"
},
]
How would I loop through this array and pull out the array index # of the object where the property "name" is equal to "evaluating" (i.e. - array element #1)?
Try Array.prototype.findIndex:
const services = [
{ _id: 1,
name: "initiating"
},
{ _id: 2,
name: "evaluating"
},
{ _id: 3,
name: "servicing"
},
];
console.log(
services.findIndex(({ name }) => name === 'evaluating')
);
let services = [{
_id: 0,
name: "initiating"
},
{
_id: 1,
name: "evaluating"
},
{
_id: 2,
name: "servicing"
},
];
let index = services.findIndex(item => item.name === "evaluating");
document.write(index);
This will return the index after searching through the each object's name property and comparing it to "evaluating".
let services = [{
_id: 0,
name: "initiating"
},
{
_id: 1,
name: "evaluating"
},
{
_id: 2,
name: "servicing"
},
]
let selected = services.findIndex(service => service.name == "evaluating")
console.log(selected)
You may use .reduce():
var services = [
{
_id: '<ObjectId>',
name: "initiating"
},
{
_id: '<ObjectId>',
name: "evaluating"
},
{
_id: '<ObjectId>',
name: "servicing"
},
];
var idx = services.reduce((x, ele, idx) => (ele.name=='evaluating') ? idx : x, -1);
console.log(idx);

Categories

Resources