How to update deeply nested array of objects JavaScript - javascript

I have the following Array of object using this information I want to update array of object with value:a without mutating it directly (I am able to solve it using index but I don't want to update it using index) below is the code that I have tried so far
ccategory.map((item) =>
item.id === payload.id
? {
...item,
categoryItems: item.categoryItems.map(
(catItem) => catItem.categoryItemID === payload.categoryItemID
// stuck here how should I update categorySubItems?
),
}
: item
),
const payload={
"id": "4476c379-2c4f-4454-b59e-cae2f62fdfe2",
"categorySubItemsID": "c2cba4d6-5635-4b5c-acf3-b93b4d435aa9",
"categoryItemID": "fdb0e86b-a2d9-4029-8988-9f50121794d3",
"value": "a"
}
MyJSON looks like this
const category=[
{
"id": "4476c379-2c4f-4454-b59e-cae2f62fdfe2",
"categoryName": "Car",
"categoryFields": [
{
"name": "Car Name",
"type": "text",
"categoryID": "e9da78fb-d349-4b03-9b77-e3cc0dc57d25"
},
{
"name": "Price",
"type": "number",
"categoryID": "c9e147a6-b5d1-424b-99bf-a973ce189322"
}
],
"categoryItems": [
{
"categoryItemID": "fdb0e86b-a2d9-4029-8988-9f50121794d3",
"categorySubItems": [
{
"categorySubItemsID": "c2cba4d6-5635-4b5c-acf3-b93b4d435aa9",
"value": "",
"label": "Car Name",
"type": "text",
"categoryLinkID": "e9da78fb-d349-4b03-9b77-e3cc0dc57d25"
},
{
"categorySubItemsID": "01d5e1e7-3927-42a6-ad05-7399a5895096",
"value": "",
"label": "Price",
"type": "number",
"categoryLinkID": "c9e147a6-b5d1-424b-99bf-a973ce189322"
}
]
},
{
"categoryItemID": "f13237d7-abfd-40d3-ae35-0b59ddf5734e",
"categorySubItems": [
{
"categorySubItemsID": "2af389b9-03bc-41d3-86bb-8bf324ca3cb3",
"value": "",
"label": "Car Name",
"type": "text",
"categoryLinkID": "e9da78fb-d349-4b03-9b77-e3cc0dc57d25"
},
{
"categorySubItemsID": "934ef505-72bb-4d64-adf1-2aa5e928a539",
"value": "",
"label": "Price",
"type": "number",
"categoryLinkID": "c9e147a6-b5d1-424b-99bf-a973ce189322"
}
]
}
]
},
{
"id": "9882b210-2d99-43a3-8aea-9f7d7c88eeda",
"categoryName": "Bike",
"categoryFields": [
{
"name": "Bike Name",
"type": "text",
"categoryID": "73bee24c-ef64-4798-bc37-5fe90cbc8de7"
}
],
"categoryItems": []
}
]

In your inner .map(), if catItem.categoryItemID === payload.categoryItemID matches, you can return a new object that has an updated categorySubItems, which you can update by creating a new array by mapping catItem.categorySubItems. When mapping the sub category items, if your categorySubItemsID matches the one from the payload object, you can return a new updated object with a new value set to that of payload.value, otherwise, you can keep the original item, eg:
ccategory.map((item) =>
item.id === payload.id
? {
...item,
categoryItems: item.categoryItems.map((catItem) =>
catItem.categoryItemID === payload.categoryItemID
? {
...catItem,
categorySubItems: catItem.categorySubItems.map(subCatItem =>
subCatItem.categorySubItemsID === payload.categorySubItemsID
? {...subCatItem, value: payload.value}
: subCatItem
)
}
: catItem
),
}
: item
),
As you can see, this can get quite unwieldy. That's why it's often useful to use something like useImmer(), which allows you to directly modify a "draft" state value in an immutable way while keeping your state updates mutable.

Related

Filter an array using conditions stored in another array

I have this array of objects:
const parks = [
{
"properties": {
"name": "Park 1",
"parking": "",
"type": "park",
"picnic_area": 1
},
},
{
"properties": {
"name": "Park 2",
"parking": 1,
"type": "park",
"picnic_area": ""
},
}
];
The page have a list of checkboxes. When user check/uncheck one of then, a function generate an object with all the selected checkboxes:
{
"parking": true,
"picnic_area": true
}
My question is: how can I use this object to generate the conditions inside a filter() function? Something like:
const parksData = parks.filter(object => {
return object_condition_1 && object_condition_2;
});
For starters, you need to rename keys in the filters object so that they match properties' keys, that is, parking, not parkings. Then,
result = parks.filter(p =>
Object.keys(filters).every(key =>
Boolean(p.properties[key]) === Boolean(filters[key]))
)
This implements an AND condition, that is, only return objects that match all filters. If you need OR instead, replace every with some.
const parks = [
{
"properties": {
"name": "Park 1",
"parking": "",
"type": "park",
"picnic_area": 1
},
},
{
"properties": {
"name": "Park 2",
"parking": 1,
"type": "park",
"picnic_area": ""
},
}
];
const filter = {
"parkings": false,
"picnic_areas": true
};
console.log(parks.filter(park => (filter.parkings && !!park.properties.parking) || (filter.picnic_areas && !!park.properties.picnic_area)));

How to group keys from a nested object?

Hya 👋
Suppose we have a dynamic object like so:
[
{
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"parent": {
"type": "page_id",
"page_id": "d4b96daf-47a3-4a04-b200"
},
"type": "child_database",
"child_database": {
"title": "Hero"
}
},
{
"object": "page",
"id": "d3022361-96d2-4e15-999e",
"parent": {
"type": "database_id",
"database_id": "089cd0d8-ccbf-4e9e-97a6"
},
},
{
"object": "block",
"id": "a0cba166-1787-4e30-8cc3",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_1",
"heading_1": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}
],
}
},
{
"object": "block",
"id": "dbfdd892-8c04-4de3-bf0e",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_2",
"heading_2": {
"rich_text": [
{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}
],
}
}
]
I would like to reconstruct this object by grouping them based on parent-child like relationship. Since every object has "parent" prop.
The desired result should be like so, where the elements that share the same parent are grouped under child array.
{
"d4b96daf-47a3-4a04-b200": {
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"type": "child_database",
"child": [{
"d3022361-96d2-4e15-999e": {
"object": "page",
"child": [{
"a0cba166-1787-4e30-8cc3": {
"object": "block",
"type": "heading_1",
"heading_1": {
"rich_text": [{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}]
}
}
},
{
"dbfdd892-8c04-4de3-bf0e": {
"object": "block",
"type": "heading_1",
"heading_2": {
"rich_text": [{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}]
}
}
}
]
}
}]
}
}
Current workaround
/**
* Generator that traverses through nested object
*/
function* traverse(xs: any[] = []): any {
for (let x of xs) {
yield x
yield* traverse(x.child || [])
}
}
/**
* If the property exists in the nested object, then return node
*/
const deepFind = (block: any, pred: any) => (obj: any) => {
for (let node of traverse([obj])) {
if (pred(node)) {
return node
}
}
}
const findById = (block: any) => (obj: any) => deepFind(block, (o: any) => o[block.id])(obj)
export default async function group(pages: Page[]) {
// stuck here 🙏
}
You can do this linearly: create a Map id=>object, iterate the list, if the parent is already on the map, add your object to the parent.child, otherwise create a placeholder object with the parent's id.
let m = new Map()
for (let obj of data) {
let dummy = {id: 'dummy', child: []}
let oid = obj.id
m.set(oid, {...dummy, ...obj, ...m.get(oid)})
let pid = obj.parent.page_id // or whatever depending on type
m.set(pid, m.get(pid) ?? dummy)
m.get(pid).child.push(obj)
}
In the end, the m.values() will contain a flat list of objects with child arrays properly populated.

Change Object Value using forEach

I am trying to change the value of object from the array but, it's not work as expected. I tried following.
const arrObj = [
{
"label": "test1",
"value": 123,
"type": "number",
"field": {
"label": "another",
"description": "abcd"
}
},
{
"label": "test2",
"value": 111,
"type": "number"
},
]
arrObj.forEach(obj => {
obj = {...obj, ...obj.field}
delete obj.field
})
console.log("after:", arrObj);
Also I found some solution that to use index but, it add index before the object.
const arrObj = [
{
"label": "test1",
"value": 123,
"type": "number",
"field": {
"label": "abcd",
"description": "abcd"
}
},
{
"label": "test2",
"value": 111,
"type": "number"
}
]
arrObj.forEach((obj, index) => {
obj[index] = {...obj, ...obj.field}
delete obj.field
})
console.log("after:", arrObj);
How can I do with forEach?
Edit:
I want to remove the field object and assign/overwrite all the property outside.
Using map and assigning the result is probably a better way of doing this, but if you want to use forEach, you need to assign to the original array inside the loop:
const arrObj = [
{
"label": "test1",
"value": 123,
"type": "number",
"field": {
"label": "another",
"description": "abcd"
}
},
{
"label": "test2",
"value": 111,
"type": "number"
},
]
arrObj.forEach(({ field, ...rest}, idx, orig) => {
orig[idx] = { ...rest, ...field }
})
console.log(arrObj);
I would use map to change an array, but you may have a reason that you wish to modify the original. You could just reassign arrObj to the output of the map.
const arrObj = [
{
"label": "test1",
"value": 123,
"type": "number",
"field": {
"label": "another",
"description": "abcd"
}
},
{
"label": "test2",
"value": 111,
"type": "number"
},
]
const newArr = arrObj.map(( obj ) => {
const {field, ...rest} = obj
return {...field, ...rest}
})
console.log("after:", newArr);

cognito user attributes to plain object

I'm trying to convert the Cognito user attributes I get from CognitoIdentityServiceProvider listUsersInGroup to plain object but I didn't found any library or AWS function that does it... then I tried to implement it by myself
That's what I came up with:
{
...user,
Attributes: user.Attributes.map((x) => ({ [x.Name]: x.Value })),
}
But that makes an array with objects and I'm trying to create an object with all the attributes...
[
{
"sub": "dasfdasfd-vcfdgfd",
},
{
"website": "aba",
},
{
"address": "new",
},
]
here is an example of the user's data (the attributes can be different from user to user):
user a:
[
{
"Name": "sub",
"Value": "dasfdasfd-vcfdgfd",
},
{
"Name": "website",
"Value": "aba",
},
{
"Name": "address",
"Value": "new",
},
{
"Name": "email_verified",
"Value": "false",
},
{
"Name": "phone_number_verified",
"Value": "false",
}
]
user b:
[
{
"Name": "custom:age",
"Value": "0",
},
{
"Name": "custom:height",
"Value": "0",
},
{
"Name": "email",
"Value": "dsafdsa#gmail.com",
}
]
You can use reduce
{
...user,
Attributes: user.Attributes.reduce((acc, { Name, Value }) => ({...acc, [Name]: Value }), {}),
}
Seems pretty simple just use loop. FYI : Array's map function always returns the array
function getAttributes(data){
let attributes = {};
for(let x of data){
attributes[x["name"]] = x["value"];
}
return attributes;
}
{
...user,
Attributes: getAttributes(user.Attributes)
}

Create new array with deleted values (React Native)

I have an array like this one :
[
Object {
"hex": "#00b2b9",
"label": "text",
"value": "364",
},
Object {
"hex": "#50690e",
"label": "text",
"value": "354",
},
Object {
"hex": "#925fa3",
"label": "text",
"value": "355"
}]
I have another array with this :
Array [
"355",
"356"
]
I'm looking to create a the first array but without the objects containing value 355 and 356. I tried with .filter()... but I'm a newbie with JS & React Native :-)
I tried some stuffs but almost every time I recreate my Array only with the values (i'm losing the objects inside)...
What I want to do is :
If I found 355 and 356 in my First Array, I delete the objects with them and I recreate my array with the only object remaining (value 364)
I was thinking about sth like that :
myFirstArray.filter(item => item.value != mySecondArray.value) but it's not a success ...
Thanks for help
The previous answers involve iterating over your id's array many times.
A more performant solution would be to store the id's in a set and then use that combined with a filter to produce your new array.
const arr = [
{
"hex": "#00b2b9",
"label": "text",
"value": "364",
},
...,
{
"hex": "#925fa3",
"label": "text",
"value": "355"
}];
const ids = ["354", "355"];
const idSet = new Set(ids);
const output = arr.filter(e => !idSet.has(e.value));
var firstArray = [{
"hex": "#00b2b9",
"label": "text",
"value": "364",
},
{
"hex": "#50690e",
"label": "text",
"value": "354",
},
{
"hex": "#925fa3",
"label": "text",
"value": "355"
}]
var secondArray = [
"355",
"356"
]
var thirdArray = firstArray.filter(item => secondArray.includes(item.value))
console.log(thirdArray)
You are nearly there, just use Array#includes() to determine whether or not the value of an item is in the second array:
myFirstArray.filter(item => !mySecondArray.includes(item.value))
let myFirstArray = [{
"hex": "#00b2b9",
"label": "text",
"value": "364",
},
{
"hex": "#50690e",
"label": "text",
"value": "354",
},
{
"hex": "#925fa3",
"label": "text",
"value": "355"
}
]
let mySecondArray = [
"355",
"356"
]
console.log(
myFirstArray.filter(item => !mySecondArray.includes(item.value))
)

Categories

Resources