Can I stringify an object in an array during FindIndex? - javascript

I have an object that contains a few key value pairs, one of which is an array. I am trying to find the index of said array, where the 'options' properties matches the options properties of another object. The function I have attached works properly with just the product ID, but it does not work when trying to compare the options object with the newObj.options.
The Object
parsedCart = {
id:1592539903073,
products: [{
productID: 1001,
name: 'Test Product'
qty: 1,
options: {
Color: "Red",
}
}]
}
The Object I wish to compare to
newObj = {
productID: 1001,
name: 'Test Product',
qty: 1,
options: {
Color: "Red",
}
}
The Function
function productExists(newObj) {
return (
newObj.productID === productID &&
JSON.stringify(newObj.options) === JSON.stringify(options)
);
}
let exists = parsedCart.products.findIndex(productExists);
console.log(exists);

I understand that you want to find the index of the products in parsedCart according to the data in newObj. And created a new items in parsedCart options for the example
If i have interpreted your question correctly i think the reason for the error is that you try to use the properties productID and options in the context of parsedCart.products in findIndex
Please try the following example
const parsedCart = {
id: 1592539903073,
products: [
{
productID: 1001,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
{
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
],
};
const newObj = {
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
};
const index = parsedCart.products.findIndex(
(product) =>
newObj.productID === product.productID &&
newObj.options.Color === product.options.Color
);
console.log(index);
Update 0
Compare options using JSON.stringify()
const parsedCart = {
id: 1592539903073,
products: [
{
productID: 1001,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
{
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
},
],
};
const newObj = {
productID: 1002,
name: "Test Product",
qty: 1,
options: {
Color: "Red",
},
};
const index = parsedCart.products.findIndex(
(product) =>
newObj.productID === product.productID &&
JSON.stringify(newObj.options) === JSON.stringify(product.options)
);
console.log(index);

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

Find items by id's from another array and add to them new property

I want to find all objects in items array which is on groupedItems array by 'id' and add to each item isGrouped: true property.
const items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const groupedItems = [
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
so the result should be:
items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2", isGrouped:true },
{ id: 3, name: "item3", isGrouped: true }
];
any ideas ?
You can iterate over the items array using map, creating an isGrouped property which is the result of calling findIndex on the item.id property in the groupedItems id values:
const items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const groupedItems = [
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const result = items.map(item => ({
...item,
isGrouped : groupedItems.findIndex(g => g.id == item.id) >= 0
}
));
console.log(result);
Note that I've added isGrouped as false for those objects which are not grouped, rather than omitting the property. It seems that it should be easier to just test a boolean value of a property rather than checking whether the property exists. If you really want to omit the property, you could do something like this:
const items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const groupedItems = [
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const result = items.map(item =>
groupedItems.find(g => g.id == item.id) ? { ...item, isGrouped : true } : item
);
console.log(result);

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

create an new array of objects from two array of objects in vanilla JS

I have two array of objects:
watches = [
{
id: "1",
label: "Rolex",
color: "UUID_OF_SILVER",
},
{
id: "2",
label: "Omega",
color: "UUID_OF_BLACK",
},
{
id: "3",
label: "Zenith",
color: "UUID_OF_SILVER",
},
];
watch_colors = [
{
id: "UUID_OF_BLACK",
label: "Black",
},
{
id: "UUID_OF_SILVER",
label: "Silver",
},
];
Now I need to print this in console using vanilla JS
Rolex Silver
Omega Black
Zenith Silver
I think I need to filter these two arrays by the common value in them both which is UUID and then make a new array and map on it to show data, but I am not sure how to approach in code.
This is what I have done so far
function watch() {
watches.map((watch) => {
console.log(watch.label);
});
}
watch();
function color() {
watch_colors.map((color) => {
console.log(color.label);
});
}
color();
Just loop though watches, find the matching color from watch_colors with color value in watches array is same as id value in watch_colors array. i.e, color.id === watch.color
const watches = [
{
id: "1",
label: "Rolex",
color: "UUID_OF_SILVER",
},
{
id: "2",
label: "Omega",
color: "UUID_OF_BLACK",
},
{
id: "3",
label: "Zenith",
color: "UUID_OF_SILVER",
},
];
const watch_colors = [
{
id: "UUID_OF_BLACK",
label: "Black",
},
{
id: "UUID_OF_SILVER",
label: "Silver",
},
];
watches.forEach((watch) => {
const color = watch_colors.find((color) => color.id === watch.color);
if(color) {
console.log(`${watch.label}, ${color.label}`);
}
})
You can make it more efficient using Map to make a dictionary and then loop over watches to print the result.
const watches = [
{
id: "1",
label: "Rolex",
color: "UUID_OF_SILVER",
},
{
id: "2",
label: "Omega",
color: "UUID_OF_BLACK",
},
{
id: "3",
label: "Zenith",
color: "UUID_OF_SILVER",
},
];
const watch_colors = [
{
id: "UUID_OF_BLACK",
label: "Black",
},
{
id: "UUID_OF_SILVER",
label: "Silver",
},
];
const dict = new Map();
watch_colors.forEach(({ id, label }) => dict.set(id, label));
watches.forEach((o) => console.log(`${o.label} ${dict.get(o.color)}`));
You can use the reduce method like the following:
const newWatches = watches.map((val) => {
const color = watch_colors.find((x) => x.id === val.color);
return `${val.label} ${color.label}`;
});
console.log(newWatches);
// prints [ 'Rolex Silver', 'Omega Black', 'Zenith Silver' ]

Javascript building a nested object from a nested object

I am attempting to build a new object from an existing deep nested object. I can't seem to get my mind in recurive mode but I am running into a bit of trouble:
oldObjArr = [{
id:1,
name:"Record1"
},{
id:2,
name:"Record2"
},{
id:3,
name:"Record3",
kids:[{
id: 4,
name: "Child 3-1"
},{
id: 5,
name: "Child 3-2"
}]
}]
buildTreeNodes = (node) => {
let data = []
node.map(record=>{
record["icon"] = "..."
record["color"] = "..."
data.push(record)
record.kids && buildTreeNodes(record.kids)
})
}
let newObjArr = buildTreeNodes(oldObjArr)
This OBVIOUSLY does not work, but I can't figure out what will. The resulting object should look like this:
[{
id:1,
name:"Record1",
icon:"...",
color: "...",
},{
id:2,
name:"Record2",
icon:"...",
color: "...",
},{
id:3,
name:"Record3",
icon:"...",
color: "...",
kids:[{
id: 4,
name: "Child 3-1",
icon:"...",
color: "...",
},{
id: 5,
name: "Child 3-2",
icon:"...",
color: "...",
}]
}]
Thanks for any help.
Robert's answer is correct.
If by chance you also want to not mutate the original object, then you can do something like this.
Also using ES6 features coz why not.
const oldObjArr = [{
id: 1,
name: "Record1"
}, {
id: 2,
name: "Record2"
}, {
id: 3,
name: "Record3",
kids: [{
id: 4,
name: "Child 3-1"
}, {
id: 5,
name: "Child 3-2"
}]
}];
function transformObject(item) {
if (Array.isArray(item.kids))
return {
...item, icon: '...', color: '...',
kids: item.kids.map(transformObject)
};
else
return {...item, icon: '...', color: '...' };
}
const newArray = oldObjArr.map(transformObject);
console.log(newArray);
So you iterate over you array and take each object and then add your props to it. Then you check if kids exist and some check if is array. i use instanceof but like #Heretic Monkey point it can be Array.isArray. What more you can setup type guard on front of function check that array argument is array then this you don't have to check that if kids is type of array.
const oldObjArr = [{
id:1,
name:"Record1"
},{
id:2,
name:"Record2"
},{
id:3,
name:"Record3",
kids:[{
id: 4,
name: "Child 3-1"
},{
id: 5,
name: "Child 3-2"
}]
}]
const addKeys = arr => {
for(const obj of arr){
obj['icon'] = "test"
obj['color'] = "test"
if("kids" in obj && obj.kids instanceof Array){
addKeys(obj.kids);
}
}
}
addKeys(oldObjArr)
console.log(oldObjArr)
V2
const addKeys = arr => {
if(!Array.isArray(arr))
return;
for(const obj of arr){
if(typeof obj !== "object")
continue;
obj['icon'] = "test"
obj['color'] = "test"
if("kids" in obj){
addKeys(obj.kids);
}
}
}
Ok check this out:
buildTreeNodes = (node) => {
let data = node.map(record=>{
record["icon"] = "..."
record["color"] = "..."
if (record.kids) record.kids = buildTreeNodes(record.kids);
return record;
})
return data;
}
let newObjArr = buildTreeNodes(oldObjArr)
console.log(newObjArr)
I think this is what you were after. You have to return record with each iteration of map, and it will add it directly to data array. The recursion within works the same.
All details are commented in demo below
let objArr = [{
id: 1,
name: "Record 1"
}, {
id: 2,
name: "Record 2"
}, {
id: 3,
name: "Record 3",
kids: [{
id: 4,
name: "Child 3-1"
}, {
id: 5,
name: "Child 3-2"
}]
},
/*
An object with a nested object not in an array
*/
{
id: 6,
name: 'Record 6',
kid: {
id: 7,
name: 'Child 6-1'
}
},
/*
An object that's filtered out because it doesn't have 'id' key/property
*/
{
no: 0,
name: null
},
/*
An object that's filtered out because it doesn't have 'id' key/property BUT has a nested object that has 'id'
*/
{
no: 99,
name: 'Member 99',
kid: {
id: 8,
name: 'Scion 99-1'
}
}
];
/*
Pass an object that has the key/value pairs that you want added to other objects
*/
const props = {
icon: '...',
color: '...'
};
/*
Pass...
a single object: {obj} of objArr[]
a single key/property: 'id'
an object that contains the key/value pairs to be added to each object that has key/property of id: {props}
*/
const addProp = (obj, prop, keyVal) => {
/*
Convert {props} object into a 2D array
props = {icon: '...', color: '...'}
~TO~
kvArr = [['icon', '...'], ['color', '...']]
*/
let kvArr = Object.entries(keyVal);
/*
for Each key/value pair of kvArr[][]
assign them to the (obj} if it has ['prop']
as one of it's key/properties
(in this demo it's 'id')
*/
kvArr.forEach(([key, val]) => {
if (obj[prop]) {
obj[key] = val;
}
});
/*
Convert {obj} into a 2D array
obj = {id: 3, name: "Record 3", kids: [{ id: 4, name: "Child 3-1"}, {id: 5, name: "Child 3-2"}]}
~TO~
subArr = [['id', 3], ['name', "Record 3"], ['kids', [{id: 4, name: "Child 3-1"}, {id: 5, name: "Child 3-2"}]]
*/
let subArr = Object.entries(obj);
/*
for Each value of subArr[][] (ie ['v'])
if it's an [Array] call addProp and pass
the {obj} of subArr[][]
*/
/*
if it's an {obj} do the same as above
*/
subArr.forEach(([k, v]) => {
if (Array.isArray(v)) {
v.forEach(subObj => {
addProp(subObj, prop, keyVal);
});
} else if (v instanceof Object) {
addProp(v, prop, keyVal);
}
});
};
// Run addProp() on each {obj} of objArr[]
for (let object of objArr) {
addProp(object, 'id', props);
}
console.log(JSON.stringify(objArr, null, 2));

Categories

Resources