Javascript- Compare 2 object excluding certain keys - javascript

I'm creating 2 objects based on given values in my jest test and expect that they would be equal except of the property uuid which is generated indevidualy for each object.
uuid can be deeply nested multiple times. for example:
const object1 = { uuid: '5435443', name: 'xxx', branches: [{ uuid: '643643', children: [{ uuid: '65654' /* and so on */ }] }];
const object2 = { uuid: '7657657', name: 'xxx', branches: [{ uuid: '443444', children: [{ uuid: '09809' }] }];
How can I compare the objects, ignoring uuid property?

You can remove uuid first than compare them.
const object1 = { uuid: '5435443', name: 'xxx', branches: [{ uuid: '643643', children: [{ uuid: '65654' /* and so on */ }] }]};
const object2 = { uuid: '7657657', name: 'xxx', branches: [{ uuid: '443444', children: [{ uuid: '09809' }] }]};
const removeUuid = o => {
if (o) {
switch (typeof o) {
case "object":
delete o.uuid;
Object.keys(o).forEach(k => removeUuid(o[k]));
break;
case "array":
o.forEach(a => removeUuid(a));
}
}
}
removeUuid(object1);
removeUuid(object2);
expect(object1).toBe(object2);

I succeeded to achieve this with the following solution:
const object1 = { uuid: '5435443', name: 'xxx', branches: [{ uuid: '643643', children: [{ uuid: '65654' /* and so on */ }] }] };
const object2 = { uuid: '7657657', name: 'xxx', branches: [{ uuid: '443444', children: [{ uuid: '09809' }] }] };
const compareExcludeKeys = (object1, object2, excludeKeys = []) => {
if (Object.keys(object1).length !== Object.keys(object2).length) return false;
return Object.entries(object1).reduce((isEqual, [key, value]) => {
const isValueEqual = typeof value === 'object' && value !== null
? compareExcludeKeys(value, object2[key], excludeKeys)
: excludeKeys.includes(key) || object2[key] === value;
return isEqual && isValueEqual;
}, true);
};
console.log(compareExcludeKeys(object1, object2, ['uuid']));

You can use expect.any to ignore uuid attribute. Just make sure that the attributes are existing, and the uuid is a string:
describe("Compare 2 objects", () => {
it("should ...", () => {
const actual = { uuid: '5435443', name: 'xxx', branches: [{ uuid: '643643', children: [{ uuid: '65654' }] }] };
const expected = {
uuid: expect.any(String), // <--
name: 'xxx',
branches: [{ uuid: expect.any(String), children: [{ uuid: expect.any(String) }] }]
};
expect(actual).toEqual(expected);
});
});

Related

How do I remove object (element) in Javascript object?

How do I remove an element from a JavaScript object by ID?
For instance I have to remove 004 or 007:
const obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
i am trying to like this, find id but what should be next. It is expected to remove id from the object.
const removeById = (obj = {}, id = '') => {
console.log('obj: ', obj)
const search = obj.children.find(o => o.id === id)
console.log('##search: ', search)
if(search) {
console.log('## parent id: ', obj.id)
...
}
if (obj.children && obj.children.length > 0) {
obj.children.forEach(el => removeById(el, id));
}
}
removeById(obj, '007')
You can use findIndex to get the location in the array.
To remove an element from an array you need to use splice.
You can then loop over the children with some and check the children's children. Using some, you can exit out when you find the id so you do not have to keep looping.
let obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
const removeById = (parentData, removeId) => {
// This is only the parent level!
// If you will never delete the first level parent, this is not needed
if (parentData.id === removeId){
Object.keys(data).forEach(key => delete parentData[key]);
return true;
}
function recursiveFind (children) {
// check if any of the children have the id
const index = children.findIndex(({id}) => id === removeId);
// if we have an index
if (index != -1) {
// remove it
children.splice(index, 1);
// say we found it
return true;
}
// Loop over the chldren check their children
return children.some(child => recursiveFind(child.children));
}
return recursiveFind(parentData.children);
}
removeById(obj,'004');
removeById(obj,'007');
console.log(obj)
We can use a recursive function to do it
let obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
const removeById = (data,id) =>{
if(data.id === id){
delete data.id
delete data.children
return
}
data.children.forEach(d =>{
removeById(d,id)
})
}
removeById(obj,'006')
console.log(obj)
Update: not leave an empty object after removing
let obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
let match = false
const removeById = (data,id) =>{
match = data.some(d => d.id == id)
if(match){
data = data.filter(d => d.id !== id)
}else{
data.forEach(d =>{
d.children = removeById(d.children,id)
})
}
return data
}
let data = [obj]
console.log(removeById(data,'004'))

How to remove objects from root of array if a duplicated is found in a Children object

The current code works with no problem. I like to know if there is a cleaner or better way to remove objects from the root of my array if a value is found somewhere in a nested object. I want to remove the object
const baseArray = [
{
title: 'Test folder',
UUID: '54F5E250-1C5F-49CC-A963-5B7FD8884E48',
Children: [
{
title: 'nothing',
UUID: 123,
},
]
},
{
title: 'nothing',
UUID: 123,
},
]
const currArray = baseArray
const removeRootUUID = (baseArray, currArray) => {
const delInRoot = uuidRef => {
baseArray.forEach((obj, i) => {
if (obj.UUID === uuidRef) {
baseArray.splice(i, 1);
}
});
};
for (const obj of currArray) {
if (obj.Children) {
obj.Children.forEach(node => {
delInRoot(node.UUID);
});
removeRootUUID(baseArray, obj.Children);
}
}
};
output I am looking for is below. (because UUID is founded in root and in another nested object)
{
title: 'Test folder',
UUID: '54F5E250-1C5F-49CC-A963-5B7FD8884E48',
Children: [
{
title: 'nothing',
UUID: 123,
},
],
}

How to change all occurrences of an object key in an array of objects

I have this sample data:
const data = [
{
id: 1,
title: 'Sports',
menus: [
{
id: 2,
title: 'Basketball',
menus: [
{
id: 3,
title: 'NBA',
},
{
id: 4,
title: 'NCAA',
},
{
id: 5,
title: 'G-League',
},
],
},
],
},
{
id: 100,
title: 'Names',
menus: [],
},
];
I want to change all the menus keys into children, so the result would be:
const result = [
{
id: 1,
title: 'Sports',
children: [
{
id: 2,
title: 'Basketball',
children: [
{
id: 3,
title: 'NBA',
},
{
id: 4,
title: 'NCAA',
},
{
id: 5,
title: 'G-League',
},
],
},
],
},
{
id: 100,
title: 'Names',
children: [],
},
];
I'm trying with this code:
const replacer = { menus: 'children' };
const transform = useCallback(
(obj) => {
if (obj && Object.getPrototypeOf(obj) === Object.prototype) {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [replacer[k] || k, transform(v)]));
}
return obj;
},
[replacer]
);
but it only changes the keys at the first level. How can I make it work?
You can use a recursive function that makes use of destructuring:
const replaceKey = arr =>
arr.map(({menus, ...o}) =>
menus ? {...o, children: replaceKey(menus)} : o);
const data = [{id: 1,title: 'Sports',menus: [{id: 2,title: 'Basketball',menus: [{id: 3,title: 'NBA',},{id: 4,title: 'NCAA',},{id: 5,title: 'G-League',},],},],},{id: 100,title: 'Names',menus: [],},];
console.log(replaceKey(data));
To provide the old/new key dynamically, use the following variant:
const replaceKey = (arr, source, target) =>
arr.map(({[source]: v, ...o}) =>
v ? {...o, [target]: replaceKey(v, source, target)} : o);
const data = [{id: 1,title: 'Sports',menus: [{id: 2,title: 'Basketball',menus: [{id: 3,title: 'NBA',},{id: 4,title: 'NCAA',},{id: 5,title: 'G-League',},],},],},{id: 100,title: 'Names',menus: [],},];
console.log(replaceKey(data, "menus", "children"));
This code assumes that values for the given key are arrays. If for some reason their values could be something else, then the code needs a bit more extension:
const data = [{id: 1,title: 'Sports',menus: [{id: 2,title: 'Basketball',menus: [{id: 3,title: 'NBA',},{id: 4,title: 'NCAA',},{id: 5,title: 'G-League',},],},],},{id: 100,title: 'Names',menus: 13,},];
const replaceKey = (arr, source, target) =>
Array.isArray(arr) ? arr.map(({[source]: value, ...o}) =>
value !== undefined ? {...o, [target]: replaceKey(value, source, target)} : o
) : arr;
console.log(replaceKey(data, "menus", "children"));
To see the effect of this code, the value for the very last menus key was changed to 13.
If the object is not big:
let data=[{id:1,title:'Sports',menus:[{id:2,title:'Basketball',menus:[{id:3,title:'NBA',},{id:4,title:'NCAA',},{id:5,title:'G-League',},],},],},{id:100,title:'Names',menus:[],},];
data = JSON.parse(JSON.stringify(data).replace(/"menus"\:/g,'"children":'))
console.log(data)
check this package: paix: that's take original source object and desired keys replacement then return a new object with desired keys, ex:
npm i paix
import { paix } from 'paix';
const data = [
{
id: 1,
title: 'Sports',
menus: [
{
id: 2,
title: 'Basketball',
menus: [
{
id: 3,
title: 'NBA',
},
],
},
],
},
{
id: 100,
title: 'Names',
menus: [],
},
];
const keys_swap = {menus: "children"};
const result = data.map(i => paix(i, keys_swap));

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

Collect flat array of deep nested data

I have dictionary:
var objectSchemasList = {
1: [
{
name: 'list_field1_1',
uuid: 'uuid1',
fieldObjectSchemaId: 2
},
{
name: 'list_field1_2',
uuid: 'uuid2',
fieldObjectSchemaId: null
},
],
2: [
{
name: 'list_field2_1',
uuid: 'uuid3',
fieldObjectSchemaId: null
},
{
name: 'list_field2_2',
uuid: 'uuid4',
fieldObjectSchemaId: null
},
],
3: [
{
name: 'list_field3_1',
uuid: 'uuid5',
fieldObjectSchemaId: 1
},
{
name: 'list_field3_2',
uuid: 'uuid6',
fieldObjectSchemaId: null
},
],
}
And array of related data to it:
const objectSchemaFields = [
{
name: 'field_1',
uuid: '_uuid1',
fieldObjectSchemaId: null
},
{
name: 'field_2',
uuid: '_uuid2',
fieldObjectSchemaId: null
},
{
name: 'field_3',
uuid: '_uuid3',
fieldObjectSchemaId: 1
},
];
It means that every object schema field can contain inside themselves other fields. That are linked by fieldObjectSchemaId. This mean that objectSchemaFields[2] element use objectSchemasList[objectSchemaFields[2].fieldObjectSchemaId]. That also uses objectSchemasList[2] and so on. It can be nested infinitely. I want to get flat array from this structure. Here i tried. Final array should be flat and has only path, name, uuid properties. Where path consists of concatenation of parent name and all nested child names splitted by point. For example result should be:
const result = [
{
path: 'field_1',
name: 'field_1',
uuid: '_uuid1',
},
{
path: 'field_2',
name: 'field_2',
uuid: '_uuid2',
},
{
path: 'field_3',
name: 'field_3',
uuid: '_uuid3',
},
{
path: 'field_3.list_field1_1',
name: 'list_field1_1',
uuid: 'uuid1',
},
{
path: 'field_3.list_field1_1.list_field2_1',
name: 'list_field2_1',
uuid: 'uuid3',
},
{
path: 'field_3.list_field1_1.list_field2_2',
name: 'list_field2_2',
uuid: 'uuid4',
},
{
path: 'field_3.list_field1_2',
name: 'list_field1_2',
uuid: 'uuid2',
}
]
It's not a good use case for map, because you still need to return the original object together with child objects, and you need to flatten it afterwards. Better stick with plain old array variable, or use reduce if you want to be fancy.
var output = [];
function processObject(path, obj) {
path = path.concat([obj.name]);
output.push({
path: path.join("."),
name: obj.name,
uuid: obj.uuid,
});
var schema = objectSchemasList[obj.fieldObjectSchemaId];
if (schema) {
schema.forEach(processObject.bind(null, path));
}
}
objectSchemaFields.forEach(processObject.bind(null, []));
https://jsfiddle.net/m8t54bv5/
You could reduce the arrays with a recursive call of a flattening function.
function flat(p) {
return function (r, { name, uuid, fieldObjectSchemaId }) {
var path = p + (p && '.') + name;
r.push({ path, name, uuid });
return (objectSchemasList[fieldObjectSchemaId] || []).reduce(flat(path), r);
};
}
var objectSchemasList = { 1: [{ name: 'list_field1_1', uuid: 'uuid1', fieldObjectSchemaId: 2 }, { name: 'list_field1_2', uuid: 'uuid2', fieldObjectSchemaId: null }], 2: [{ name: 'list_field2_1', uuid: 'uuid3', fieldObjectSchemaId: null }, { name: 'list_field2_2', uuid: 'uuid4', fieldObjectSchemaId: null }], 3: [{ name: 'list_field3_1', uuid: 'uuid5', fieldObjectSchemaId: 1 }, { name: 'list_field3_2', uuid: 'uuid6', fieldObjectSchemaId: null }] },
objectSchemaFields = [{ name: 'field_1', uuid: '_uuid1', fieldObjectSchemaId: null }, { name: 'field_2', uuid: '_uuid2', fieldObjectSchemaId: null }, { name: 'field_3', uuid: '_uuid3', fieldObjectSchemaId: 1 }],
result = objectSchemaFields.reduce(flat(''), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources