Update single object in localStorage - javascript

I want to update a single Object in my localStorage. I made a detail page, where I can submit new values (progress and value)
When I want to update the value, it changes the value in both objects. How can I change just one object.
Here is my deployment link.(its work in progress)
https://mastery-app.herokuapp.com/
This is my localStorage array:
skills[
{
"title": "Sewing",
"imageSrc": "images.unsplash.com",
"description": "Make your own clothes",
"category": "crafting",
"progress": 500,
"isDone": false,
"rank": 0,
"value": 0
},
{
"title": "Crocheting",
"imageSrc": "images.unsplash.com",
"description": "Interlock loops of yarn",
"category": "crafting",
"progress": 500,
"isDone": false,
"rank": 0,
"value": 0
}
]
This is how I update the localStorage:
const update = skills.map((skills) => {
skills.title === skills.title;
const updateProgress = skills.progress - value;
const rankNumber = parseInt(ranking);
const updateRank = skills.rank + rankNumber;
console.log(updateRank);
const updateValue = skills.value + value;
return {
title: skills.title,
rank: updateRank,
description: skills.description,
progress: updateProgress.toFixed(1),
imageSrc: skills.imageSrc,
category: skills.category,
isDone: false,
value: updateValue,
};
});
localStorage.setItem('skills', JSON.stringify(update));

You may consider using the find method to find the object you want to update. map is not the right function to be used for your use case.
Also skills.title === skills.title; has no effect at all (Maybe you wanted to use an if statement to do some kind of filtering by using title but that always would return true). Please remove that.
Now, I don't exactly know which field are you going to use to search for the object you want to update, but it has to be unique. If none of the fields in the objects are unique you should consider adding an unique id field in the skills objects. But if title is unique you can use the title to search. Then you can do something like the pseudo code below:
const toUpdate = skills.find(skill => skill.title === your_title_here)
toUpdate.field_to_update_1 = some_value_1
toUpdate.field_to_update_2 = some_value_2
localStorage.setItem('skills', JSON.stringify(skills))
Please also check the MDN docs to see how map, find and other array methods work and some of their use cases.

Related

Array - What's the best way to check a value exists in one of the array elements (React / JS)

So this is an example data array that I will get back from backend. There are a few use cases as shown below and I want to target based on the subscription values in the array.
Example: 1
const orgList = [
{ id: "1", orgName: "Organization 1", subscription: "free" },
{ id: "2", orgName: "Organization 2", subscription: "business" },
];
In the example 1 - when array comes back with this combination - there will be some styling and text to target the element with subscription: free to upgrade its subscription
Example 2:
const orgList = [
{ id: "1", orgName: "Organization 1a", subscription: "pro" },
{ id: "2", orgName: "Organization 2a", subscription: "business" },
];
Example 3:
const orgList = [
{ id: "1", orgName: "Organization 1b", subscription: "free" },
];
In the example 3 - when array comes back with only one element - there will be some styling and text to target the element say to upgrade its subscription
At the moment, I'm simply using map to go over the array that I get back like so:
{orgList.map((org) => (...do something here)} but with this I'm a bit limited as I don't think this is the best way to handle the 3 use cases / examples above.
Another idea is too do something like this before mapping but this:
const freeSubAndBusinessSub = org.some(org => org.subscription === 'free' && org.subscription === "business")
but doesn't seem to work as it returns false and then I'm stuck and not sure how to proceed after..
So my question is what's the best way to approach this kind of array to target what do to with the elements based on their values?
You mention that using .map() is limited, but you don't expand on it. Logically what it sounds like you want is a separate list for each type to act upon. You can accomplish this using .filter() or .reduce(), however, in this case .map() is your friend.
// Example 1
const free = orgList.filter(org => org.subscription === 'free');
const business = orgList.filter(org => org.subscription === 'business');
free.map(org => /* do free stuff */);
business.map(org => /* do business stuff */);
// Example 2
const subscriptions = orgList.reduce((all, cur) => {
if (!all.hasOwnProperty(cur.subscription)) {
all[cur.subscription] = [];
}
all[cur.subscription].push(cur);
return all;
}, {});
subscriptions['free'].map(org => /* do free stuff */);
subscriptions['business'].map(org => /* do business stuff */);
// Example 3
orgList.map(org => {
switch(org.subscription) {
case 'free':
/* do free stuff */
break;
case 'business':
/* do business stuff */
break;
}
})
You'll notice that in all the examples, you still need to map on the individual orgs to perform your actions. Additionally, with the first two examples, you'll be touching each element more than once, which can be incredibly inefficient. With a single .map() solution, you touch each element of the list only once. If you feel that you do free stuff actions become unwieldy, you can separate them out in separate functions.

Adding a new object into a nested array

I want to add a new object for each nested array. I'm calling this function any time I add a product to my orderintake:
add2order(productID, productName, productRatePlans) {
this.orderIntake.push({ productID, productName, productRatePlans });
let i = 0;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges.forEach(element => {
i++;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].quantity = this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].defaultQuantity;
});
}
this is an example response from the server:
{
"id": "8adc8f996928b9a4016929c59b943a8f",
"sku": "SKU-00006778",
"Partner_Account_ID__c": null,
"productRatePlans": [
{
"id": "8adce4216928c28d016929c59bff3372",
"status": "Active",
"name": "Enterprise",
"description": null,
"effectiveStartDate": "2016-02-26",
"effectiveEndDate": "2029-02-26",
"productRatePlanCharges": [
{
"id": "8adc8f996928b9a4016929c59d183a92",
"name": "USAGE_COUNTER_2",
"type": "Usage",
"model": "Volume",
"uom": "Each",
"pricingSummary": [
"Up to 5000 Each: USD0 flat fee"
],
"pricing": [
{
...
}
],
"defaultQuantity": null,
"applyDiscountTo": null,
"discountLevel": null,
"discountClass": null,
...
"financeInformation": {
..,
}
}
]
}
],
"productFeatures": [
{
...
}
]
}
The data is being retrived this way from an external REST backend so unfortunately I can't initialize the data including the new property...
so in every productRatePlanCharges there should be 1 new object 'quantity'.
How can I add this field to every productRatePlanCharges?
Right now I'm getting: ERROR
TypeError: Cannot read property 'productRatePlanCharges' of undefined
And how can I make sure I'm always adding this to the last orderIntake element? Don't mind productRatePlans there is only 1 in each orderintake...
thanks for your support!
Here you have to create productDetails object with inititalised array like below so that you won't get the error.
add2order(productID, productName, productRatePlans) {
// Create object like below
let productDetails = { productID : productID, productName : productName, productRatePlans : productRatePlans
}
this.orderIntake.push(productDetails);
let i = 0;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges.forEach(element => {
i++;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].quantity = this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].defaultQuantity;
});
}
Hope this will help!
as you used Angular you probably use Typescript too. I recommend that you create a model like your incoming model and there define your quantity: number inside productRatePlanCharges object. then map the incoming data to your own model. therefore you will have a quantity=0 in your model that you can change it later in a loop.
If you want to continue with your own way take a look at this:
Add new attribute (element) to JSON object using JavaScript
there is no problem to add an element to current model almost like you did, and the problem might be somewhere else as your error refers to existence of productRatePlanCharges!
as you used forEach I prefer to use that 'element' and double iterating with i++; is not a good idea to me.
this might be better:
element.quantity = element.defaultQuantity;

Angular ng-model & filter using nested data vs top layer

I have a list of data displayed on my page that is broken down into divs. Each div represents an array of data in my object, pretty common.
I am trying to add a text box to my page where I can filter out the data and it will narrow down the results shown on the page as more data is entered into the text box.
For that, I added a filter on my ngFor like so: *ngFor="let x of data | filter: filterString".
My text-box then uses ngModel to filter that data down:
<input type="text" class="form-control" placeholder="Filter..." name="ruleFilter" id="ruleFilter" [(ngModel)]="filterString" (keyup)="onFilter($event)">
The issue I am having is that the filter seems to only be working with the top layer of data in my object. For example, the data below is what one of the results looks like in my ngFor loop. I can search Omaha just fine since its in the top level and it filters it down correctly.
However, If I look for something like Campus which is nested inside Attribute, it doesn't find it in the filter and no results are shown.
{
"RuleParentID": "618",
"RuleVersionID": "18",
"MappedValue": "1",
"ProcessingOrder": 1,
"KeyID": "1",
"Value": "Omaha",
"IsRuleRetired": "0",
"UserImpactCount": "0",
"Attribute": [
{
"AttributeID": "6",
"AttributeName": "Campus",
"Operator": {
"OperatorID": "3",
"OperatorName": "In List",
"SqlOperator": "IN"
},
"AttributeValue": [
{
"AttrValue": "1",
"Value": "Omaha",
"IsValueRetired": "0",
"disabled": "False"
}
]
},
{
"AttributeID": "14",
"AttributeName": "Grade",
"Operator": {
"OperatorID": "1",
"OperatorName": "Greater Than",
"SqlOperator": ">"
},
"AttributeValue": [
{
"AttrValue": "14",
"Value": "14",
"IsValueRetired": "0",
"disabled": "False"
}
]
}
]
}
Is there any way to have the model look at all layers of the object for my binding instead of just the top layer (which I only assume its doing at this time) ?
Update: Here is a plunker of what my basic setup is like: https://plnkr.co/edit/eywuWmPRseUkmVPbTEOf?p=preview
You will see the data model that searches by the top level properties just fine, but when I search for something nested, I don't get any results back.
If I understand well the question, I think that to flat the data will help you:
var flattenObject = function(ob) {
var toReturn = {};
for (var i in ob) {
if (!ob.hasOwnProperty(i)) continue;
if ((typeof ob[i]) == 'object') {
var flatObject = flattenObject(ob[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + '.' + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
};
let newData = flattenObject(data);
Code source: https://gist.github.com/penguinboy/762197
To achieve expected result , use below option
1.In your component below variable
jsonVal:any=JSON; // for using JSON.stringify and indexOf
Use *ngIf to filter value from input with indexOf
<input type="text" [(ngModel)]="filterString">
<div *ngFor="let data of result">
<div *ngIf="jsonVal.stringify(data).indexOf(filterString)!= -1">{{data| json}}</div>
</div>
code sample for reference - https://stackblitz.com/edit/angular-ht2afv?file=app/app.component.html
Just for testing , I have added another Object with Campus2 and Omaha2
When filtering on a nested property of data you can use the map function or similar.
This will be in your component and not the template. Filtering using pipes in the template is discouraged by the Angular team for performance reasons.
Instead I would do something like this:
const data = [{//your data}]
let filteredData = [];
data.map(val => {
if (val.Attribute.filter(name => name.AttributeName === "Foo").length > 0) {
filteredData.push(val)
}
});
I am assuming your data is an array of objects.
Beware I am mutating my data object. To avoid this you do this:
const data = [{//your original data}]
const dataToFilter = JSON.Parse(JSON.stringify(data))
This will make copy of your data without references to your original object. Useful if you want to clear your filter. Not useful if your data object contains functions.
On re-reading your question I think this is not the solution you were looking for but rather a method to look anywhere in the data. For this you should probably flatten your data as suggested by Zelda7. Another approach would be to extend a filtering method to explicitly filter on all relevant fields.

On/off toggle for filtering content React

This React code is filtering variables according to the "tag" which it contains (as seen in the list array).
However, I cannot toggle the filter variables (tags) on/off.
I want to be able to turn certain filters on/off, and have just those filters apply.
How is this achieved?
My entire code is in this codepen (
http://codepen.io/yarnball/pen/GqbyWr?editors=1010)
I believe I have to some how add it to the array on line 79 (below), but I have not had success with this
Line 79:
selectTag: function (tag) {
this.setState({
displayedCategories: this.state.displayedCategories.concat([tag]),
$push : [newObject]
});
},
My data looks like this:
"title": "Into the Wild",
"tag": [
{
"name": "Movie",
"taglevel": 1,
"id": 1
},
{
"name": "Adventure",
"taglevel": 2,
"id": 30
},
{
"name": "Book",
"taglevel": 1,
"id": 2
}
],
"info": []
}
In order to toggle the filters, you will need to check for the existence of the tag in the existing displayedCategories, look through the array for the tag, and then either remove it or add it in.
It is normally my preference to try to be functional so that assignment cannot cause confusion, so I will use a mostly functional style.
First to check for the presence of the tag we can use a filter operation.
var filteredCategories = this.state.displayedCategories
.filter(function (existingTag) {
return existingTag.taglevel !== tag.taglevel ||
existingTag.id !== tag.id;
});
So we now have a list of tags that are filtered to only include those that don't match the passed tag. We can check if the filtered list is the same size as the old list to see if we removed one. Alternatively, we could have filtered the other way around to see if we needed to remove one using some.
if (filteredCategories.length === this.state.displayedCategories.length){
// tag wasn't present, add it
} else {
// tag was present, use filtered list.
}
As I said above, I prefer functional, so we can do it slightly differently:
var newCategories = filteredCategories.length === this.state.displayedCategories.length ?
filteredCategories.concat([tag]) :
filteredCategories;
and then we need to set state:
this.setState({
displayedCategories: newCategories,
});
To combine those together:
var filteredCategories = this.state.displayedCategories
.filter(function (existingTag) {
return existingTag.taglevel !== tag.taglevel ||
existingTag.id !== tag.id;
});
var newCategories = filteredCategories.length === this.state.displayedCategories.length ?
filteredCategories.concat([tag]) :
filteredCategories;
this.setState({
displayedCategories: newCategories,
});

Update object with given ID embed in array without restructuring Mongo database

I've got the following document named "clients" which includes id, name and list of projects (array of objects):
{
"_id": {
"$oid": "572225d997bb651819f379f7"
},
"name": "ppg",
"projects": [
{
"name": "aaa",
"job_description": "",
"projectID": 20
},
{
"name": "bbbb",
"job_description": "",
"projectID": 21
}
]
}
I would like to update "job_description" of project with given "projectID" like this:
module.exports.saveJobDesc = function(client, idOfProject, textProvided) {
db.clients.update({ name: client},
{ $set: {'projects.0.job_description': textProvided }});
};
But instead of hardcoded index "0" of array I want to find specific project using "projectID". Is there a way to achieve this without changing the structure of collection and/or document?
If you want to update the "job_description" where name="ppg" and project_id=20 then you can use below mongo query:-
db.clients.update({ "name":"ppg","projects.projectID":20 },{$set: {"projects.$.job_description": "abcd"}})
Please let me know if any thing else is required
You cannot update multiple array elements in single update operation, instead you can update one by one which takes time depends upon number of elements in array and number of such documents in collection. see New operator to update all matching items in an array
db.test2.find().forEach( function(doc) {
var projects = doc.projects;
for(var i=0;i<projects.length;i++){
var project = projects[i];
if(project.projectID == 20){
var field = "projects."+i+".job_description";
var query = {};
query[field] = "textasdsd";
db.test2.update({ _id: doc._id},{ $set:query});
}
}
})

Categories