Related
Trying to turn an array of objects into a nested object. Is there a good method for this? and how do I make it depending on the array length?
Working but is not universal:
https://codesandbox.io/s/thirsty-roentgen-3mdcjv?file=/src/App.js
What I have:
sorting: [
{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
},
{
"id": "gender",
"options": [
"male",
"female"
]
}
]
What I want:
value: {
"Region": "Oklahoma",
"HighestDegree": {
"HighSchool": {
"male": null,
"female":null
},
"Undergraduate":{
"male": null,
"female":null
}
//and so on...
}
}
The code beneath works but is hardcoded for only two different options. I want it to be able to nest the length of the array. So lets say another object was age it would be {"HighSchool":{male:{"<25":null,"25-35":null}}} etc..
function testSortingArray() {
let sorting = [
{
id: "HighestDegree",
options: ["HighSchool", "Undergraduate", "Bachelor", "Master", "Doctor"]
},
{
id: "gender",
options: ["male", "female"]
}
];
let GoalArray = {};
if (sorting.length > 0) {
sorting[0].options.map((firstArray) => {
let currObject = {};
sorting[1].options.map((secondOption) => {
currObject[secondOption] = null;
});
GoalArray[firstArray] = currObject;
});
}
return GoalArray;
}
console.log(testSortingArray());
You can do it with a recursive function.
The function below reduces every options array to an object, and then continues populating that object if there are rest elements left from the original sorting array.
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
Besides the reduce() method, the code above makes use of object and array destructuring and spread syntax.
Complete snippet:
const sorting = [{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
}, {
"id": "gender",
"options": [
"male",
"female"
]
}, {
"id": "age",
"options": [
"<25",
"25-35"
]
}];
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
console.log(result);
Restructuring array of objects to new array
Problem
There’s an array of objects that contains plain strings and might contain nested arrays as well. We want to create a new Array that will contain a node for each item in the array and separate nodes for each array item connected to its parent. Each parent node should have the following structure:
{
id: uuidv4(),
position: { x: 0, y: 0 },
data: { label: <item data goes here> }
}
Each array node with the following schema above, should also have a connection edge item added to the array with the following properties:
{
id: ‘e<array item Id>-<parentId>’,
source: <array item Id>,
target: <parentId>,
}
Example
We have the following array of objects for example:
[
{
"author": "John Doe",
"age": 26,
"books": [
{
"title": "Book 1"
},
{
"title": "Book 2",
"chapters": [
{
"title": "No Way Home",
"page": 256
}
]
}
]
}
]
The expected output is:
[
{
"id": "1",
"data": {
"label": {
"author": "John Doe",
"age": 26,
}
}
},
{
"id": "2",
"data": {
"label": "books" // key of array
}
},
{
"id": "3",
"data": {
"label": {
"title": "Book 1"
}
}
},
{
"id": "4",
"data": {
"label": {
"title": "Book 2"
}
}
},
{
"id": "5",
"data": {
"label": "chapters" // key of array
}
},
{
"id": "6",
"data": {
"label": {
"title": "No Way Home",
"page": 256
}
}
},
{
"id": "e2-1",
"source": "2",
"target": "1"
},
{
"id": "e3-2",
"source": "3",
"target": "2"
},
{
"id": "e4-2",
"source": "4",
"target": "2"
},
{
"id": "e5-4",
"source": "5",
"target": "4"
},
{
"id": "e6-5",
"source": "6",
"target": "5"
}
]
First of all, I would not be answering if there was not already a good answer. Please, on StackOverflow, always show your own attempts and explain where you got stuck. But since there is already an answer, I think this version might be a bit simpler.
Second, I'm assuming this output format is some sort of directed graph, that the first half is your list of vertices and the second half a list of edges. If so I don't know if your output format is constrained here. But if you had the option, I would think a better structure would be an object with vertices and edges properties, each containing an array. You might then not need the edges' ids. And the code could also be simplified.
This version first converts to an intermediate structure like this:
[
{id: "1", data: {label: {author: "John Doe", age: 26}}, children: [
{id: "2", data: {label: "books"}, children: [
{id: "3", data: {label: {title: "Book 1"}}, children: []},
{id: "4", data: {label: {title: "Book 2"}}, children: [
{id: "5", data: {label: "chapters"}, children: [
{id: "6", data: {label: {title: "No Way Home"}}, children: []}
]}
]}
]}
]}
]
Then we flatten that structure into the first section of the output and use it to calculate the relationships (edges?) between nested nodes to go in the second section.
The code looks like this:
const transform = (input) => {
const extract = (os, nextId = ((id) => () => String (++ id)) (0)) => os .map ((o) => ({
id: nextId(),
data: {label: Object .fromEntries (Object .entries (o) .filter (([k, v]) => !Array .isArray (v)))},
children: Object .entries (o) .filter (([k, v]) => Array .isArray (v)) .flatMap (([k, v]) => [
{id: nextId(), data: {label: k}, children: extract (v, nextId)},
])
}))
const relationships = (xs) =>
xs .flatMap (({id: target, children = []}) => [
... children .map (({id: source}) => ({id: `e${source}-${target}`, source, target})),
... relationships (children),
])
const flatten = (xs) =>
xs .flatMap (({children, ...rest}) => [rest, ... flatten (children)])
const res = extract (input)
return [...flatten (res), ... relationships (res)]
}
const input = [{author: "John Doe", age : 26, books: [{title: "Book 1"}, {title: "Book 2", chapters: [{title: "No Way Home", page: 256}]}]}]
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
We use three separate recursive functions. One does the recursive extract into that intermediate format. Along the way, it adds id nodes using a nextId stateful function (something I usually avoid, but seems to simplify things here.) Then flatten simply recursively lifts the children to sit alongside their parents. And relationships (again recursively) uses the ids of the parent- and child-nodes to add an edge node.
Using these three separate recursive calls is probably less efficient than some other solutions, but I think it leads to much cleaner code.
One has to choose a self recursive approach which in a generic way can process both, array-items and object-entries. Also, while the recursive process takes place, one not only has to create and collect the consecutively/serially numbered (the incremented id value) data nodes, but one in addition needs to keep track of every data node's parent reference in order to finally concatenate the list of edge items (as the OP calls it) to the list of data nodes.
function flattenStructureRecursively(source = [], result = [], tracker = {}) {
let {
parent = null, edgeItems = [],
getId = (id => (() => ++id))(0),
} = tracker;
const createEdgeItem = (id, pid) => ({
id: `e${ id }-${ pid }`,
source: id,
target: pid,
});
const putNodeData = node => {
result.push(node);
if (parent !== null) {
edgeItems.push(createEdgeItem(node.id, parent.id));
}
// every data node is a parent entity too.
parent = node;
};
if (Array.isArray(source)) {
result.push(
...source.flatMap(item =>
flattenStructureRecursively(item, [], {
getId, parent, edgeItems,
})
)
);
} else {
let {
dataNode,
childEntries,
} = Object
.entries(source)
.reduce(({ dataNode, childEntries }, [key, value]) => {
if (value && (Array.isArray(value) || (typeof value === 'object'))) {
// collect any object's iterable properties.
childEntries.push([key, value]);
} else {
// aggregate any object's non iterable
// properties at data node level.
(dataNode ??= {
id: getId(),
data: { label: {} }
}).data.label[key] = value;
}
return { dataNode, childEntries };
}, { dataNode: null, childEntries: [] });
if (dataNode !== null) {
putNodeData(dataNode);
}
childEntries
.forEach(([key, value]) => {
// every object's iterable property is supposed
// to be created as an own parent entity.
dataNode = {
id: getId(),
data: { label: key },
};
putNodeData(dataNode);
result.push(
...flattenStructureRecursively(value, [], {
getId, parent, edgeItems,
})
);
});
}
if (parent === null) {
// append all additionally collected edge items
// in the end of all the recursion.
result.push(...edgeItems);
}
return result;
}
console.log(
flattenStructureRecursively([{
author: "John Doe",
pseudonym: "J.D.",
books: [{
title: "Book 1",
}, {
title: "Book 2",
chapters: [{
title: "No Way Home",
page: 256,
}],
}],
age: 26,
}])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I'm having trouble copying the mapped return of a onSnapshot listener from firebase inside an array (But very IMPORTANT : I don't want to use the state syntax):
useEffect(() => {
let copyChats = [];
const unsubscribe = db.collection('chats').onSnapshot(snapshot =>
(
console.log(snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
}
)))
)
)
return unsubscribe;
}, [])
This code returns correctly the content in the console:
Array [
Object {
"data": Object {
"chatName": "presidentielle",
},
"id": "8DRKgDCW54Zt4yf1anoR",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "9MudmtUfi9nGiWMpwHGk",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "Hi5a8FpDaf7EwwwpmOXn",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "ZnX3jaRkrJN4oiNCC3un",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "dPruNs46X0jbhr0sL7xk",
},
Object {
"data": Object {
"chatName": "test chat",
},
"id": "vxs7RdSBER83TOCPHko1",
},
]
Array [
Object {
"data": Object {
"chatName": "presidentielle",
},
"id": "8DRKgDCW54Zt4yf1anoR",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "9MudmtUfi9nGiWMpwHGk",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "Hi5a8FpDaf7EwwwpmOXn",
},
Object {
"data": Object {
"chatName": "titi",
},
"id": "ZnX3jaRkrJN4oiNCC3un",
},
Object {
"data": Object {
"chatName": "essais",
},
"id": "dPruNs46X0jbhr0sL7xk",
},
Object {
"data": Object {
"chatName": "test chat",
},
"id": "vxs7RdSBER83TOCPHko1",
},
]
But what I want to do is to copy this exact data model in an the array copyChats, and I can't do it. I've tried all kinds of spread and push syntax.
useEffect(() => {
let copyChats = [];
const unsubscribe = db.collection('chats').onSnapshot(snapshot =>
(
copyChats = snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
}
))
)
)
console.log(copyChats);
return unsubscribe;
}, [])
The only thing I'm able to get is an empty array !like the code above which give me this return:
Array []
If you have any Idea. Thank you..
The problem is not where you access the array, but when you access it. If you run the code in a debugger, or add a console.log("Hello") inside he call, you'll see that the console.log(copyChats) actually runs before copyChats = snapshot.docs.map ever executes. So that explains why the array is empty: it hasn't been populated yet.
I don't want to use the state syntax
React's state is precisely the mechanism to pass data that is asynchronously loaded to the UI. If you want to render the data from the database in the UI, using state is the correct approach.
Also see:
React native call function in text element
React Not Updating Render After SetState
Using Map in React with firebase
I'm working on a react app that mimics a retail website. My main page displays an item, and below has card components of related products. When I click a button on one of the related products, I open a comparison modal that compares features of the current product and the clicked upon product. I figured that to accomplish this, I would create an array of the combined features of the clicked on product and the main page product. I've been struggling to get create an array of objects, where each unique feature has an object with data inside about the features and which product the feature belongs to.
As of right now, I've been able to get an array of all the features that the two products have, but this array has repeats if the products have overlapping features. This makes me unsure of how to render the comparison table because I was planning on mapping over the array and creating a table row for each feature. My current code to format these features is as follows:
formatFeatures: (currentProd, clickedProd) => {
let combinedFeatures = [];
if (clickedProd.features) {
clickedProd.features.forEach(feature => {
let obj = {}
let vals = Object.values(feature);
obj[vals[0]] = [vals[1], clickedProd.id]
combinedFeatures.push(obj)
})
}
currentProd.features.forEach(feature => {
let obj = {}
let vals = Object.values(feature);
obj[vals[0]] = [vals[1], currentProd.id]
combinedFeatures.push(obj)
})
let formattedFeatures = combinedFeatures.reduce((allFeatures, feature) => {
if (Object.keys(feature) in allFeatures) {
allFeatures = [allFeatures[Object.keys(feature)]].concat(feature);
} else {
allFeatures.push(feature);
}
return allFeatures;
}, [])
The result of this is:
[{
"Fabric": ["100% Cotton", 28214]
}, {
"Cut": ["Skinny", 28214]
}, {
"Fabric": ["Canvas", 28212]
}, {
"Buttons": ["Brass", 28212]
}]
This is pretty close to what I am looking for, where I have an array of objects that contain information about the feature and product id of the product, but the repeat in "Fabric" is something I'm struggling to sort out. Ideally, the result would look like this:
[{
"Fabric": ["100% Cotton", 28214],
["Canvas", 28212]
}, {
"Cut": ["Skinny", 28214]
}, {
"Buttons": ["Brass", 28212]
}]
If anyone can help guide me as to how to change my formatting function to accomplish this, I'd be very grateful. Alternatively, if anyone knows a better way to dynamically format a table with a single row for each unique feature given my current result, that would be great too.
The data coming into my helper function is as follows:
CurrentProd:
{
"id": 28212,
"name": "Camo Onesie",
"slogan": "Blend in to your crowd",
"description": "The So Fatigues will wake you up and fit you in. This high energy camo will have you blending in to even the wildest surroundings.",
"category": "Jackets",
"default_price": "140.00",
"created_at": "2021-07-10T17:00:03.509Z",
"updated_at": "2021-07-10T17:00:03.509Z",
"features": [{
"feature": "Fabric",
"value": "Canvas"
}, {
"feature": "Buttons",
"value": "Brass"
}]
}
ClickedProd:
{
"name": "Morning Joggers",
"category": "Pants",
"originalPrice": "40.00",
"salePrice": null,
"photo": "https://images.unsplash.com/photo-1552902865-b72c031ac5ea?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=300&q=80",
"id": 28214,
"features": [{
"feature": "Fabric",
"value": "100% Cotton"
}, {
"feature": "Cut",
"value": "Skinny"
}]
}
There seems to be a bigger question of how to structure your data. You say that ideally your results would look like:
[
{
"Fabric":
["100% Cotton",28214],
["Canvas",28212]
},
{
"Cut":
["Skinny",28214]
},
{
"Buttons":
["Brass",28212]
}
]
But what you're really trying to get out of this is a combined list of rows and associated values for each item feature, if it exists. All you really need then is an array of keys for each row you want to display, and objects that let you access the needed property by that key.
The array of keys could look like this:
["Fabric", "Cut", "Buttons"]
The objects you want to access the properties using those keys, for example your CurrentProd, could be this (notice that you can access a feature by calling CurrentProd.features["FeatureName"]):
{
"id":28212,
"name":"Camo Onesie",
// ... //
"features": {
"Fabric": "Canvas",
"Buttons": "Brass"
}
}
Having said that, to get those things you can get the array of keys, which we'll call allFeatureKeys, by reducing over a combined array of CurrentProd.features and ClickedProd.features:
const allFeatureKeys = [
...CurrentProd.features,
...ClickedProd.features
].reduce((acc, cur) => {
return acc.findIndex(cur.feature) > -1 ? [...acc, cur.feature] : acc
},
[]
);
And you can modify your CurrentProd to the above data shape by reducing over the array of its features, let's call this modifiedCurrentProd:
const modifiedCurrentProd = {
...CurrentProd,
features: CurrentProd.features.reduce((acc, cur) => {
return {...acc, [cur.feature]: cur.value}
}, {})
}
Repeat that for a modifiedClickedProd object, then you have both CurrentProd.features and ClickedProd.features values available for a lookup when you create your table values.
As an example only, since I don't know your react structure or what data you want to display, you can then render the values in the table rows mapping over the keys to make each row, and for each feature key, you access the value from the modifiedCurrentProd or modifiedClickedProd object's features property:
<div id="table">
{allFeatureKeys.map((featureKey) => {
return <div id="table-row">
<div>{featureKey}</div>
<div>
{
modifiedCurrentProd.features[featureKey] !== undefined
? modifiedCurrentProd.id
: "n/a"
}
</div>
<div>
{
modifiedClickedProd.features[featureKey] !== undefined
? modifiedClickedProd.id
: "n/a"
}
</div>
</div>
})}
</div>
Firstly the target data structure needs to be fixed/optimized. It looks like the OP does concentrate on something which is based on a generic Feature (like Fabric, Cut, Buttons) whereas such feature values seem to be associated more with the Product. Thus for one and the same feature the values are unique to the product feature. In order to not loose the product information, a target format's feature item needs to reflect its related product's id property.
A viable and still flexible enough target data structure then might look like this ...
{
"Fabric": [{
productId: 28214,
value: "100% Cotton",
}, {
productId: 28212,
value: "Canvas",
}],
"Cut": [{
productId: 28214,
value: "Skinny",
}],
"Buttons": [{
productId: 28212,
value: "Brass",
}],
}
Any approach should start with a data-normalizing mapping-process of a product's features list where each feature item will get its product related id assigned.
Thus a feature item like { feature: "Buttons", value: "Brass" } gets mapped temporarily into { productId: 28212, feature: "Buttons", value: "Brass" }.
The two normalized data-item lists now can be concatenated and finally processed/reduced into the final target structure ...
function mergeBoundProductId(item) {
return { ...this, ...item };
}
function aggregateProductFeatureValueLists(index, productFeature) {
const { feature, ...featureValue } = productFeature;
const featureList = index[feature] ??= [];
//const featureList = index[feature] || (index[feature] = []);
featureList.push(featureValue);
return index;
}
function createIndexOfProductFeatureValues(clickedProd, currentProd) {
const { features:clickedFeatures } = clickedProd;
const { features:currentFeatures } = currentProd;
return [
...clickedFeatures.map(mergeBoundProductId, { productId: clickedProd.id }),
...currentFeatures.map(mergeBoundProductId, { productId: currentProd.id }),
].reduce(aggregateProductFeatureValueLists, {});
}
const currentProduct = {
id: 28212,
name: "Camo Onesie",
// ... more properties ...
features: [{
feature: "Fabric",
value: "Canvas",
}, {
feature: "Buttons",
value: "Brass",
}],
};
const clickedProduct = {
name: "Morning Joggers",
// ... more properties ...
id: 28214,
features: [{
feature: "Fabric",
value: "100% Cotton",
}, {
feature: "Cut",
value: "Skinny",
}],
};
console.log(
'createIndexOfProductFeatureValues(clickedProduct, currentProduct) ...',
createIndexOfProductFeatureValues(clickedProduct, currentProduct)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
The advantage of breaking the code into dedicated processes comes with easier refactoring for e.g. changed target structures like something closer to what the OP was looking for.
The changes to the reducer function are minimal. It's just two changes, each barely noticeable in its line ...
function mergeBoundProductId(item) {
return { ...this, ...item };
}
function aggregateProductFeatureValueLists(index, productFeature) {
const { feature, productId, value } = productFeature;
const featureList = index[feature] ??= [];
featureList.push([value, productId]);
return index;
}
function createIndexOfProductFeatureValues(clickedProd, currentProd) {
const { features:clickedFeatures } = clickedProd;
const { features:currentFeatures } = currentProd;
return [
...clickedFeatures.map(mergeBoundProductId, { productId: clickedProd.id }),
...currentFeatures.map(mergeBoundProductId, { productId: currentProd.id }),
].reduce(aggregateProductFeatureValueLists, {});
}
console.log(
'createIndexOfProductFeatureValues(clickedProduct, currentProduct) ...',
createIndexOfProductFeatureValues(clickedProduct, currentProduct)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
const currentProduct = {
id: 28212,
name: "Camo Onesie",
// ... more properties ...
features: [{
feature: "Fabric",
value: "Canvas",
}, {
feature: "Buttons",
value: "Brass",
}],
};
const clickedProduct = {
name: "Morning Joggers",
// ... more properties ...
id: 28214,
features: [{
feature: "Fabric",
value: "100% Cotton",
}, {
feature: "Cut",
value: "Skinny",
}],
};
</script>
The last example's purpose too is to prove the advantage of an easy to refactor code base.
Here the main function gets renamed from createIndexOfProductFeatureValues to createListOfProductFeatureValues.
It's implementation also changes likewise but only in the way how the reducer function gets invoked with its initial value.
The reducer function also does not change dramatically, only in the way of how the accumulating/aggregating collector object gets handled.
And the result is a clean array based object structure ...
function mergeBoundProductId(item) {
return { ...this, ...item };
}
function aggregateProductFeatureValueLists(collector, productFeature) {
const { feature, productId, value } = productFeature;
const { index, list } = collector;
const featureItem = index[feature] ??= { feature, values: [] };
if (featureItem.values.length === 0) {
list.push(featureItem);
}
featureItem.values.push([value, productId]);
return collector;
}
function createListOfProductFeatureValues(clickedProd, currentProd) {
const { features:clickedFeatures } = clickedProd;
const { features:currentFeatures } = currentProd;
return [
...clickedFeatures.map(mergeBoundProductId, { productId: clickedProd.id }),
...currentFeatures.map(mergeBoundProductId, { productId: currentProd.id }),
].reduce(aggregateProductFeatureValueLists, { index: {}, list: [] }).list;
}
console.log(
'createListOfProductFeatureValues(clickedProduct, currentProduct) ...',
createListOfProductFeatureValues(clickedProduct, currentProduct)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
const currentProduct = {
id: 28212,
name: "Camo Onesie",
// ... more properties ...
features: [{
feature: "Fabric",
value: "Canvas",
}, {
feature: "Buttons",
value: "Brass",
}],
};
const clickedProduct = {
name: "Morning Joggers",
// ... more properties ...
id: 28214,
features: [{
feature: "Fabric",
value: "100% Cotton",
}, {
feature: "Cut",
value: "Skinny",
}],
};
</script>
You are already looping through both once. You can get it without reducing.
const formatFeatures = (currentProd, clickedProd) => {
const formattedFeatures = {};
if (clickedProd.features) {
clickedProd.features.forEach(feature => {
const vals = Object.values(feature);
if (!formattedFeatures.hasOwnProperty(vals[0])) {
formattedFeatures[vals[0]] = [];
}
formattedFeatures[vals[0]].push([vals[1], clickedProd.id]);
});
}
currentProd.features.forEach(feature => {
const vals = Object.values(feature);
if (!formattedFeatures.hasOwnProperty(vals[0])) {
formattedFeatures[vals[0]] = [];
}
formattedFeatures[vals[0]].push([vals[1], currentProd.id]);
})
return formattedFeatures;
}
const currentProd = {
"id": 28212,
"name": "Camo Onesie",
"slogan": "Blend in to your crowd",
"description": "The So Fatigues will wake you up and fit you in. This high energy camo will have you blending in to even the wildest surroundings.",
"category": "Jackets",
"default_price": "140.00",
"created_at": "2021-07-10T17:00:03.509Z",
"updated_at": "2021-07-10T17:00:03.509Z",
"features": [{
"feature": "Fabric",
"value": "Canvas"
}, {
"feature": "Buttons",
"value": "Brass"
}]
};
const clickedProd = {
"name": "Morning Joggers",
"category": "Pants",
"originalPrice": "40.00",
"salePrice": null,
"photo": "https://images.unsplash.com/photo-1552902865-b72c031ac5ea?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=300&q=80",
"id": 28214,
"features": [{
"feature": "Fabric",
"value": "100% Cotton"
}, {
"feature": "Cut",
"value": "Skinny"
}]
};
console.log(formatFeatures(currentProd, clickedProd));
.as-console-wrapper { min-height: 100%!important; top: 0; }
This is Browser localstorage Object referred as dataset
let dataset = localStorage.getItem('dataset') !== null ? leech : [];
[
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd"
}
]
This is the initial data object available I want to add more field to a particular id.
This is what I want :
[
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd",
"status":1
}
]
This my code to find the particular id
const user = dataset.find(user => user.id == 456);
Now how can I add status to user and update the user in the dataset?
You've already found the user by using Array.prototype.find() so all you need to do then is add the status property
// const dataset = JSON.parse(localStorage.getItem("dataset"))
const dataset = [{"id":123,"name":"abc"},{"id":456,"name":"bcd"}]
const user = dataset.find(({ id }) => id === 456)
if (user) {
user.status = 1
}
console.info(dataset)
.as-console-wrapper { max-height: 100% !important }
If you then want to store the modified data back into localStorage, use localStorage.setItem() and JSON.stringify()
localStorage.setItem("dataset", JSON.stringify(dataset))
If you want keep dataset initial value, and would like to get a new array, you can use Array.reduce() method.
const dataset = [
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd"
}
]
const output = dataset.reduce((acc, cur) => {
if (cur.id === 456) cur.status = 1;
acc.push(cur);
return acc;
}, []);
console.log(output);
If you want to update dataset, you can use Array.forEach() method.
const dataset = [
{
"id": 123,
"name": "abc"
},
{
"id": 456,
"name": "bcd"
}
]
dataset.forEach(user => {
if (user.id === 456) user.status = 1;
});
console.log(dataset);
You could do with Array#Findindex with callback return function. so could pass the originaldata,searchId and update object. In this method you could updated object easily
Why i suggest findIndex
Because findindex not running entire loop or iteration. If the match
detect on first iteration they will break the loop and returning the
result.For long iteration its more faster than other loop (reduce,forEach)
const data = [ { "id": 123, "name": "abc" }, { "id": 456, "name": "bcd" } ]
function update(dataset,searchId,addtionObject){
let ind = dataset.findIndex(({id}) => id == searchId);
dataset[ind] = {...dataset[ind],...addtionObject}; //join the new and old array
return dataset
}
console.log(update(data,456,{status:1}))
If you want to create new state objet, you can use immer for that.
Immer will produce the nextState based on the mutations to the draft state.
import produce from "immer";
const baseState = [
{
id: 123,
name: "abc",
},
{
id: 456,
name: "bcd",
},
];
const nextState = produce(baseState, (draftState) => {
draftState[1].status = 1;
});