Javascript transform array of object structure - javascript

I am trying to transform an array of objects structure from looking like this:
[
{
id: 1,
val: "bool",
name: "somename",
entities: [
{
id: 1,
name: "varchar",
type: "string"
}
]
},
{
id: 2,
val: "bool",
name: "somename",
entities: [
{
id: 1,
name: "varchar",
type: "string"
}
]
}
]
Into this:
[
{
id: 1,
val: "bool",
name: "somename",
entitiesName: "varchar",
entitiesType: "string"
},
{
id: 2,
val: "bool",
name: "somename",
entitiesName: "varchar",
entitiesType: "string"
}
]
So more or less I want to take two values from entities and make them into key/values in the root object instead.
I have tried using Object.entries(data).map() but I am stuck

You could use a destruction for collecting all wanted properties and build a new object.
var array = [{ id: 1, val: "bool", name: "somename", entities: [{ id: 1, name: "varchar", type: "string" }] }, { id: 2, val: "bool", name: "somename", entities: [{ id: 1, name: "varchar", type: "string" }] }],
result = array.map(
({ id, val, name, entities: [{ name: entitiesName, type: entitiesType }] }) =>
({ id, val, name, entitiesName, entitiesType })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Use Array.map()
Create two new key value pairs entitiesName and entitiesType
Delete the entities key
var data = [{
id: 1,
val: "bool",
name: "somename",
entities: [{
id: 1,
name: "varchar",
type: "string"
}]
},
{
id: 2,
val: "bool",
name: "somename",
entities: [{
id: 1,
name: "varchar",
type: "string"
}]
}
];
data = data.map(
(el) => {
el.entitiesName = el.entities[0].name;
el.entitiesType = el.entities[0].type;
delete el.entities;
return el;
}
);
console.log(data);

To avoid modification on source array.
var data = [ { id: 1, val: "bool", name: "somename", entities: [ { id: 1, name: "varchar", type: "string" } ] }, { id: 2, val: "bool", name: "somename", entities: [ { id: 1, name: "varchar", type: "string" } ] }];
var result = data.map((o) => {
var obj = {...o, 'entitiesName': o.entities[0].name, 'entitiesType': o.entities[0].type };
delete obj.entities;
return obj;
});
console.log(data);
console.log(result);
.as-console-wrapper {
max-height: 100% !important
}

You can do that by using Array.map function of array and spread operator
var data = [{
id: 1,
val: "bool",
name: "somename",
entities: [{
id: 1,
name: "varchar1",
type: "string"
}]
},
{
id: 2,
val: "bool",
name: "somename",
entities: [{
id: 1,
name: "varchar2",
type: "string"
}]
}
];
var result = data.map(
(el) => {
const { entities, ...noentities } = el;
return {...noentities, entitiesName :el.entities[0].name ,entitiesType:el.entities[0].type}
}
);
console.log(result)

Related

How to compare two array of objects and return the not matching object?

obj1 is the original object and obj2 is the changed object. I want to get the key , value pair and the type of all the changed object inside obje2 array of objects.
So, I need something like this where if "name" or "id" value is different in obj2 return the object along with the type.
changedObj = [
{
type:"mobile",
name:"Temple Runs",
id:2259
},
{
type:"pc",
name:"Pubgs",
id:222
}
]
obj1 = [
{
type: "mobile",
games: [
{
name: "Temple Run",
id: 2259,
},
{
name: "Subway Surfer",
id: 2271,
},
{
name: "Pubg",
id: 2272,
},
],
},
{
type: "pc",
games: [
{
name: "Pubg",
id: 222,
},
{
name: "Fortnite",
id: 2274,
},
{
name: "Nfs",
id: 2272,
},
],
},
];
obj2 = [
{
type: "mobile",
games: [
{
name: "Temple Runs",
id: 2259,
},
{
name: "Subway Surfer",
id: 2271,
},
{
name: "Pubg",
id: 2272,
},
],
},
{
type: "pc",
games: [
{
name: "Pubgs",
id: 222,
},
{
name: "Fortnite",
id: 2274,
},
{
name: "Nfs",
id: 2272,
},
],
},
];
How to achieve something like this ?
In order to find the difference, you will need to:
Map all of the updated platforms (type and games)
Filter the updated games and locate the original game by ID
Flat-map the games in each platform and include the type
const main = () => {
const delta = diff(changed, data);
console.log(delta);
};
const diff = (updated, original) =>
updated
.map(({ type, games }) => ({
type,
games: games
.filter(({ name, id }) => original
.find(platform => platform.type === type).games
.find(game => game.id === id)?.name !== name)
}))
.flatMap(({ type, games }) =>
games.map(({ name, id }) =>
({ name, id, type })));
const data = [{
type: "mobile",
games: [
{ name: "Temple Run", id: 2259 },
{ name: "Subway Surfer", id: 2271 },
{ name: "Pubg", id: 2272 }
],
}, {
type: "pc",
games: [
{ name: "Pubg", id: 222 },
{ name: "Fortnite", id: 2274 },
{ name: "Nfs", id: 2272 }
]
}];
const changed = [{
type: "mobile",
games: [
{ name: "Temple Runs", id: 2259 },
{ name: "Subway Surfer", id: 2271 },
{ name: "Pubg", id: 2272 }
],
}, {
type: "pc",
games: [
{ name: "Pubgs", id: 222 },
{ name: "Fortnite", id: 2274 },
{ name: "Nfs", id: 2272 }
]
}];
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }

Filter Array of objects with nested array

so I am trying to set up a nested filter on an array of objects.
The thing is that the filter is applied inside the object on a key that is another array of objects.
here is the code:
const items = [
{ name: "123", id: 1, value: true, arr: [{ id: 1 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 2 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 3 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 4 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 5 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 6 }] },
];
const newArray = items.filter((objects) => {
objects.arr.filter((item) => {
if (item.id === 2) {
return objects;
}
});
});
console.log(newArray);
I 'm not sure where to put the return because in this situation i just get an empty array.
You need to check the nested array contains the wanted id and return the result to the filter method.
const
items = [{ name: "123", id: 1, value: true, arr: [{ id: 1 }] }, { name: "456", id: 2, value: false, arr: [{ id: 2 }] }, { name: "456", id: 2, value: false, arr: [{ id: 3 }] }, { name: "456", id: 2, value: false, arr: [{ id: 4 }] }, { name: "456", id: 2, value: false, arr: [{ id: 5 }] }, { name: "456", id: 2, value: false, arr: [{ id: 6 }] }],
result = items.filter(({ arr }) => arr.some(({ id }) => id === 2));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use Array#some to check if current arr has an element with id equal to 2:
const items = [ { name: "123", id: 1, value: true, arr: [{ id: 1 }] }, { name: "456", id: 2, value: false, arr: [{ id: 2 }] }, { name: "456", id: 2, value: false, arr: [{ id: 3 }] }, { name: "456", id: 2, value: false, arr: [{ id: 4 }] }, { name: "456", id: 2, value: false, arr: [{ id: 5 }] }, { name: "456", id: 2, value: false, arr: [{ id: 6 }] } ];
const newArray = items.filter(({ arr = [] }) =>
arr.some(({ id }) => id === 2)
);
console.log(newArray);
As you contains only one object in arr then you can access this object by using [0] index.
Working Demo :
const items = [
{ name: "123", id: 1, value: true, arr: [{ id: 1 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 2 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 3 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 4 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 5 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 6 }] },
];
const newArray = items.filter((obj) => {
if (obj.arr[0].id === 2) {
return obj;
}
});
console.log(newArray);
I answered you as per the issue you are facing but if you have multiple objects in your arr then you can go ahead with Array.some() method as suggested by other answers.

How to filter nested arrays by searching

I have an array of objects that I want to filter by comparing a nested property to a search term.
For example:
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
]};
What I'm trying to achieve is if I search for 'nodata' for example my result should be
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
}
]
}
];
Another option if I search for 'spa' my result should be
let array = [
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
}
]
}
];
I have been super confused and I decided to get some help. Thank you for your helps guys!
The following function should do the trick for you:
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
It recursively goes through the data array, looks for any entries that have the specified search term, and if so, places it into the newly constructed object. It will preserve the nested shape of the object, which may or may not be what is needed. Feel free to tweak the algorithm to your own needs.
let allData = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
];
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
console.log('----- Search: nodata')
console.log(JSON.stringify(searchData(allData, 'nodata'), null, 2))
console.log('----- Search: spa')
console.log(JSON.stringify(searchData(allData, 'spa'), null, 2))

Cartesian array based on array of objects in Javascript

I'm looking for method to find cartesian array based on array of objects.
Basicly I've seen solutions like that:
Cartesian product of multiple arrays in JavaScript
but I'm not sure how to modify it to work on object property (in my case on property "value").
For instance my input:
let arr1 = [
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 2,
type: "attribute",
value: "arr1-attr2"
}
];
let arr2 = [
{
id: 3,
type: "attribute",
value: "arr2-attr1"
}
];
let arr3 = [
{
id: 4,
type: "attribute",
value: "arr3-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
];
Expected output:
output = [
[
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 4,
type: "attribute",
value: "arr3-attr1"
}
],
[
{
id: 2,
type: "attribute",
value: "arr1-attr2"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
],
[
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 4,
type: "attribute",
value: "arr3-attr1"
}
],
[
{
id: 2,
type: "attribute",
value: "arr1-attr2"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
]
];
Just take the single arrays as items of a collection of this arrays and perform the algorithm on this data set.
var arr1 = [{ id: 1, type: "attribute", value: "arr1-attr1" }, { id: 2, type: "attribute", value: "arr1-attr2" }],
arr2 = [{ id: 3, type: "attribute", value: "arr2-attr1" }],
arr3 = [{ id: 4, type: "attribute", value: "arr3-attr1" }, { id: 5, type: "attribute", value: "arr3-attr2" }],
result = [arr1, arr2, arr3]
.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

filter inside forEach does no work properly when finds double objects

I try to get each object of an array and compare it to the objects of another array. If they match, remove the object from the second array.
Strange thing is that if an object is found two times in an array, that object is not filtered.
I want to compare newdata to existing. If an object of newdata has the same id and cat, it will not be in the new array.
existing is
var existing = [{
name: "John",
values_: {
id: 5,
cat: true
}
},
{name: "Jake",
values_: {
id: 3,
cat: true
}
},
{
name: "Alice",
values_: {
id: 2,
cat: false
}
}
];
newdata is
var newdata = [{
name: "Mike",
properties: {
id: 1,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{
name: "Alice",
properties: {
id: 2,
cat: false
}
}
];
and my filter is
existing.forEach((existingitem, existingindex, existingfeatures) => {
newdata2 = newdata.filter(item => (
existingitem.values_.id != item.properties.id &&
existingitem.values_.cat != item.properties.cat
));
});
console.log('newdata2 - ',newdata2);
The logic thing is for newdata2 to have only Mike . The problem is that I can see Jake two times. Jake should not be there, its already in existing.
If I edit newdata like so (no doubles)
var newdata = [{
name: "Mike",
properties: {
id: 1,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
} ,
{
name: "Alice",
properties: {
id: 2,
cat: false
}
}
];
I still can see Jake in newdata2. But why?
Please help me fix this. Is it my filter or the way filter works? Based on the criteria, I should only get Mike at the end. Please advice.
Thank you
var existing = [{
name: "John",
values_: {
id: 5,
cat: true
}
},
{name: "Jake",
values_: {
id: 3,
cat: true
}
},
{
name: "Alice",
values_: {
id: 2,
cat: false
}
}
];
var newdata = [{
name: "Mike",
properties: {
id: 1,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{name: "Jake",
properties: {
id: 3,
cat: true
}
},
{
name: "Alice",
properties: {
id: 2,
cat: false
}
}
];
existing.forEach((existingitem, existingindex, existingfeatures) => {
newdata2 = newdata.filter(item => (
existingitem.values_.id != item.properties.id &&
existingitem.values_.cat != item.properties.cat
));
});
console.log('newdata2 - ',newdata2);
... think about a filter (outer) and every (inner) based approach instead of forEach and filter - maybe that makes it easier to think about a correct implementation.
var existingItemList = [{ name: "John", values_: { id: 5, cat: true }}, { name: "Jake", values_: { id: 3, cat: true }}, { name: "Alice", values_: { id: 2, cat: false }}];
var newItemList = [{ name: "Mike", properties: { id: 1, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Alice", properties: { id: 2, cat: false }}];
var itemList = newItemList.filter(function (newItem) { // filter `newItem` only
return existingItemList.every(function (existingItem) { // if it does not exist
return ( // in `existingItemList`.
//(newItem.name !== existingItem.name) &&
(newItem.properties.id !== existingItem.values_.id)
);
});
});
console.log('itemList : ', itemList);
.as-console-wrapper { max-height: 100%!important; top: 0; }
EDIT
follow up, referring this comment of mine ...
your comparison condition just does not fit what you are really searching/looking for.
If one still assumes that the OP wants to filter a new item only if it does not already exist in another list that it is going to be compared to ...
... one has to write a matcher function that maps and compares item fields in one and the same time.
This comparator/matcher then has to be used in a way that it will filter only the very new item that does not equal any other already existing item.
This can be achieved by a slight change to the former approach from above ...
function doesExistingItemMatchBoundNewItem(existingItem) {
var newItem = this;
return (
(newItem.properties.id === existingItem.values_.id)
&& (newItem.properties.cat === existingItem.values_.cat)
);
}
var existingItemList = [{ name: "John", values_: { id: 5, cat: true }}, { name: "Jake", values_: { id: 3, cat: true }}, { name: "Alice", values_: { id: 2, cat: false }}];
var newItemList = [{ name: "Mike", properties: { id: 1, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Jake", properties: { id: 3, cat: true }}, { name: "Alice", properties: { id: 2, cat: false }}];
var itemList = newItemList.filter(function (newItem) {
return !existingItemList.some(doesExistingItemMatchBoundNewItem.bind(newItem));
});
console.log('itemList : ', itemList);
.as-console-wrapper { max-height: 100%!important; top: 0; }
You could take a Set and filter the known items.
var existing = [{ name: "John", values_: { id: 5, cat: true } }, { name: "Jake", values_: { id: 3, cat: true } }, { name: "Alice", values_: { id: 2, cat: false } }],
newdata = [{ name: "Mike", properties: { id: 1, cat: true } }, { name: "Jake", properties: { id: 3, cat: true } }, { name: "Jake", properties: { id: 3, cat: true } }, { name: "Alice", properties: { id: 2, cat: false } }],
eSet = new Set(existing.map(({ values_: { id, cat } }) => [id, cat].join('|'))),
result = newdata.filter(({ properties: { id, cat } }) => !eSet.has([id, cat].join('|')));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories