JavaScript / AngularJS modify property in array - javascript

I have the following AngularJS model:
$scope.Model = {
Users : [{
UserId: '',
FirstName: '',
LastName: ''
}],
Products :[{
ProductId: '',
Price: ''
}]
};
If I populate this array with N users, and one user has id=1, how can I update that specific user (with id=1) the property LastName?
So for example if I will get a new AngularJS model:
$scope.UserToUpdate ={
UserId: 1,
LastName: "Smith"
};
I want to loop through the $scope.Model array and update the user with id=1 but only the FirstName property.
P.S. I don't know at what position the target user object in the array it is so basically can be at $scope.Model.Users[0] or $scope.Model.Users[1] or $scope.Model.Users[10] or at $scope.Model.Users[N] ...

You can just loop through your list of users
for (var i = 0; i < $scope.Model.Users.length; i++) {
if($scope.Model.Users[i].UserId === $scope.UserToUpdate.UserId) {
$scope.Model.Users[i].LastName = $scope.UserToUpdate.LastName;
break;
}
}
EDIT: Actually harish's answer is on to something too. Here's another solution using $filter:
var matchedUsers = $filter('filter')($scope.Model.Users, { UserId: $scope.UserToUpdate.UserId });
if (matchedUsers.length > 0) {
matchedUsers[0].LastName = $scope.UserToUpdate.LastName;
}
And don't forget to add the $filter service as a parameter in your controller declaration for this second solution.

$scope.UserToUpdate =
$scope.Model.Users.filter(function(user) { return user.FirstName == "test"; })[0];
BTW: you can add a check if the user exists..

you can use $filter
var user = $filter('filter')($scope.Model.Users, 'UserId == 1');
you are read more about $filter('filter') here

Try this! working demo http://plnkr.co/edit/scyV79HqqA7nOG9h4ezH?p=preview . Please check the console log.
angular.forEach($scope.Model[0].Users, function(value1, key1) {
var i = 0;
angular.forEach(value1, function(value, key) {
if (key == 'UserId' && $scope.UserToUpdate.UserId == value) {
$scope.Model[0].Users[i].LastName = $scope.UserToUpdate.LastName;
}
i++;
});
});
The above code updating the Model object LastName property based on UserToUpdate object (id=1)

Related

angularjs filters with an OR operation

I have data array like this :
$scope.data = [{
name: 'joseph',
statarray: [{
status: 'Online',
status: 'Offline',
}],
active: 'yes'
},
{
name: 'arnold',
statarray: [{
status: 'Offline'
}],
active: 'no'
},
{
name: 'john',
statarray: [{
status: 'Online'
}],
active: 'yes'
}
];
$scope.findObjectByKey = function(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
};
$scope.Online = function(array){
var obj = $scope.findObjectByKey(array, 'status', 'Online');
return obj;
}
$scope.Offline = function(array){
var obj = $scope.findObjectByKey(array, 'status', 'Offline');
return obj;
}
The functions $scope.Online and $scope.Offline sorts the data according to the status Online and Offline.
Here's my view :
I have these two checkboxes as filters :
<input ng-true-value='Online' ng-false-value='' type="checkbox" ng-model="online" type="checkbox">Online
<input ng-true-value='Offline' ng-false-value='' type="checkbox" ng-model="offline" type="checkbox">Offline
<div ng-repeat="user in data|filter:online|filter:offline">
<p>{{user.name}}</p>
</div>
Currently when I click the checkbox corresponding to Online it displays the user joseph and john whose status is Online and when I click the checkbox corresponding to Offline it displays the users joseph and arnold whose status are Offline. This much is working perfectly. But when I click both the filter buttons it only displays joseph as joseph has both Online and Offline status. So an AND operation is being applied here. But I want an OR operation here. So when I click both the filter buttons I should get the output as joseph,arnold and john in the view. Any clue on how can I do it?
First, your statarray seems wrong, considering you declared one object with two properties with the same name, first we should move it to something like an array only containing the status strings ex. ['Online', 'Offline'].
You are executing the filter function only using the latest filter selected.
You need to think in a different approach to aggregate your selected filters,
something like create an filter obj.
filter = {
online: true,
offline: false
}
and iterate over then to display your data
$scope.filterArray = function(array, key, value) {
var filtered = [];
for (var i = 0; i < array.length; i++) {
var shouldInclude = false;
shouldInclude |= ($scope.filter.online && array[i].statarray.indexOf('Online') >= 0);
shouldInclude |= ($scope.filter.offline && array[i].statarray.indexOf('Offline') >= 0);
if (shouldInclude) {
filtered.push(array[i]);
}
}
return filtered;
};
This is just one possible approach, if you are able to use ES6 functions this become even simpler.
# pravin navle-
Are you sure its working same as you described below code? Because when I tried to replicated same functionality it works only for Offline and not for Online as well as Both Checked.

Nested foreach/forloops, looping through JSON to extract values

I'm going to pre-face this with saying i'm not sure this is the best approach so other approaches are greatly appreciated
End Goal: To store a list of products and the toppings purchased by calling the woocommerce API and using the response data
I'm calling the woocommerce REST api that provides me a good chunk of JSON data back. In the JSON are line_items. These are the products purchased. Nested in line_items are meta_data, this is the toppings for example tomato or sauce.
Attached an image of the JSON
So what i'm trying to do is create something like this
var testOrderItems =
[{
title: "Fried Chicken Burger",
meta: [
"Lettuce",
"cheese slice",
"kethcup"
]
},
{
title: "Beef Burger",
meta: [
"Lettuce",
"cheese slice",
"kethcup"
]
}
]
which will follow my schema for oder items
var orderItems = new Schema({
title: {type: String, required: true},
meta: [{type: String}]
});
So to do this, i figured I would just do a forloop or foreach through the JSON to get all the product names and their meta. Getting actual values is easy. The hard part is creating the array or JSON object that I can then store, i'm just not sure how to create it whilst in the loop. Below are a few things I tried
let fullData = JSON.parse(result)
//parsed response from woocommerce API call
fullData.line_items.forEach((product, index) => {
//for each line item get me the product
orderItems.push(product.name)
//var namey =
//push the product name to the orderItemsArray
product.meta_data.forEach(function(meta) {
//checks for string as one of the plug-ins fills the meta with more nested information and we only want the top level string
if (typeof meta.value === 'string' || meta.value instanceof String)
// it's a string
orderItems.push(meta.value)
//Onbviously won't nest the meta with the product name just on new lines
})
});
The I thought I could do it in for loops by storing an ID ref as "i" and being able to re-reference this later in the nested loop to add the meta, i got a little lost with this
var length = fullData.line_items.length
for (let i = 0; i < length; i++) {
// console.log(i);
console.log(fullData.line_items[i].name)
for (let j = 0; j < fullData.line_items[i].meta_data.length; j++) {
var metaValue = fullData.line_items[i].meta_data[j].value
if (typeof metaValue === 'string' || metaValue instanceof String) {
console.log(fullData.line_items[i].meta_data[j].value);
stringMeta = fullData.line_items[i].meta_data[j].value
//this works but has drawbacks
//1 obviously just overwrites itself each time
//2 will stop at the end of meta so won't add items without meta
finalOrderItems = {
id: i,
name: fullData.line_items[i].name,
meta: [stringMeta]
}
}
}
}
and thats where I am, feels like this should be incredibly easy but can't quite grasp it at the moment.
You could simply create the object that represents your schema first, then return it from a map of your json Object. So, it would look like the following:
let testOrderItems = fullData.line_items.map((product)=>{
let obj = { name: product.name };
obj.meta = product.meta_data.map((meta)=>{
if (typeof meta.value === 'string' || meta.value instanceof String)
return meta.value;
}).filter((value)=>!!value);
return obj;
})
console.log(testOrderItems);
Although, the if statement seems a little redundant, since the woocommerce api will simply either have meta or not. However, you may have some plugin or something which is adding more information to the meta area so i've kept it in my example.
This looks like a job for map and reduce not forEach. map will map each object of line_items into a new object and reduce will group and organize the metas by key for each object:
var orderItems = fullData.line_items.map(function(product) { // map each product in line_items
return { // into a new object
title: product.name, // with title equals to the current product's name
meta: product.meta_data.reduce(function(acc, meta) { // and metas accumulated from each meta object in the current product's meta_data array
acc[meta.key] = acc[meta.key] || []; // first, check if there is an array for the current meta's key in the group object 'acc', if not create one
acc[meta.key].push(meta.value); // add the current meta's value to that array
return acc;
}, {})
}
});
Shorter using arrow functions:
var orderItems = fullData.line_items.map(product => ({
title: product.name,
meta: product.meta_data.reduce((acc, meta) => {
acc[meta.key] = acc[meta.key] || [];
acc[meta.key].push(meta.value);
return acc;
}, {})
}));

Adding an Object to an Array

I'm struggling on adding an Object to an Array (E-commerce context).
My "tc_vars" datalayer is mapped with another datalayer which is called "wa_data". The latter sends the requested information to the first one.
An Object in that case will be a specific product and the Array will be the cart.content property :
var tc_vars = {
nav : {
siteCategory : wa_data.nav.siteCategory,
environment :wa_data.nav.environment,
siteType :wa_data.nav.siteType,
siteName :wa_data.nav.siteName,
pageName :wa_data.nav.pageName,
siteSection :wa_data.nav.siteSection,
country :wa_data.nav.country,
language :wa_data.nav.language,
template :wa_data.nav.template,
doNotTrack :window.navigator.doNotTrack,
customReferrer :wa_data.nav.customReferrer,
genomeID :wa_data.nav.genomeID,
mdmBID :wa_data.nav.mdmBID,
mdmIID :wa_data.nav.mdmIID
},
profile : {
uplayID : readCookie("user_id"),
loginStatus : ''
},
internalSearch : {
searchStatus :wa_data.internalSearch.searchStatus,
searchFilters :wa_data.internalSearch.searchFilters,
searchKeyWord :wa_data.internalSearch.searchKeyWord,
totalResults :wa_data.internalSearch.totalResults,
resultPosition :wa_data.internalSearch.resultPosition,
autoCompletion :wa_data.internalSearch.autoCompletion
},
product : {
productID :wa_data.product.productID,
unitSalePrice :wa_data.product.unitSalePrice,
salePrice :wa_data.product.salePrice,
stockAvailability :wa_data.product.stockAvailability,
salesType :wa_data.product.salesType,
costOfGood :wa_data.product.costOfGood
},
cart : {
orderID:wa_data.cart.orderID,
cartOpen:wa_data.cart.cartOpen,
cartAdd:wa_data.cart.cartAdd,
cartRemove:wa_data.cart.cartRemove,
cartView:wa_data.cart.cartView,
checkout:wa_data.cart.checkout,
purchase:wa_data.cart.purchase,
currency:wa_data.cart.currency,
paymentMethod:wa_data.cart.paymentMethod,
orderShipping:wa_data.cart.orderShipping,
orderTotalAmountDiscounted:wa_data.cart.orderTotalAmountDiscounted,
orderTotalAmountNotDiscounted:wa_data.cart.orderTotalAmountNotDiscounted,
orderTaxAmount:wa_data.cart.orderTaxAmount,
orderDiscountedAmount:wa_data.cart.orderDiscountedAmount,
orderShippingCost:wa_data.cart.orderShippingCost,
billingRegion:wa_data.cart.billingRegion,
billingCity:wa_data.cart.billingCity,
orderStatus:wa_data.cart.orderStatus,
content : [{
productID:'',
name:'',
quantity :'',
promoCode:'',
offerID:'',
salesType:'',
platform :'',
unitSalePrice:'',
salePrice:'',
stockAvailability:'',
lineItemTotalAmountDiscounted:'',
lineItemTotalAmountNotDiscounted:'',
lineItemTaxAmount:'',
lineItemDiscountedAmount:'',
lineItemShippingCost:'',
crossSell:'',
upSell:''
}]
},
tech : {
containerVersion : wa_data.tech.containerVersion
}
}
//Scanning for the content using a loop
if (typeof tc_vars.cart.content !== 'undefined' && tc_vars.nav.pageName === 'Basket'){
for(i=0; i < tc_vars.cart.content.length; i++) {
tc_vars.cart.content[i].productID = wa_data.cart.content[i].productID;
tc_vars.cart.content[i].name = wa_data.cart.content[i].name;
tc_vars.cart.content[i].quantity = wa_data.cart.content[i].quantity;
tc_vars.cart.content[i].promoCode = wa_data.cart.content[i].promoCode;
tc_vars.cart.content[i].offerID = wa_data.cart.content[i].offerID;
tc_vars.cart.content[i].salesType = wa_data.cart.content[i].salesType;
tc_vars.cart.content[i].platform = wa_data.cart.content[i].platform;
tc_vars.cart.content[i].unitSalePrice = wa_data.cart.content[i].unitSalePrice;
tc_vars.cart.content[i].salePrice = wa_data.cart.content[i].salePrice;
tc_vars.cart.content[i].stockAvailability = wa_data.cart.content[i].stockAvailability;
tc_vars.cart.content[i].lineItemTotalAmountDiscounted = wa_data.cart.content[i].lineItemTotalAmountDiscounted;
tc_vars.cart.content[i].lineItemTotalAmountNotDiscounted = wa_data.cart.content[i].lineItemTotalAmountNotDiscounted;
tc_vars.cart.content[i].lineItemTaxAmount = wa_data.cart.content[i].lineItemTaxAmount;
tc_vars.cart.content[i].lineItemDiscountedAmount = wa_data.cart.content[i].lineItemDiscountedAmount;
tc_vars.cart.content[i].lineItemShippingCost = wa_data.cart.content[i].lineItemShippingCost;
tc_vars.cart.content[i].crossSell = wa_data.cart.content[i].crossSell;
tc_vars.cart.content[i].upSell = wa_data.cart.content[i].upSell;
}
}
The problem I'm facing here is that my code is not creating a new object for each new product that is added to the cart content (with all the dedicated properties of the new object).
I've tried using a loop which scans my cart content Array but apparently it's not working (not adding a new object inside the Array). Seems like I'm missing something.
Do you guys have any ideas?
Thx a lot
J
tc_vars.cart.content[i] is undefined. You need to define it first, before filling it up.
for(i=0; i < tc_vars.cart.content.length; i++) {
tc_vars.cart.content[i] = {}; // Creates an empty object
tc_vars.cart.content[i].productID = wa_data.cart.content[i].productID; // Starts filling it
// ....
}
As an alternative (lighter syntax and faster execution), you could also write :
for(i=0; i < tc_vars.cart.content.length; i++) {
tc_vars.cart.content[i] = {
productID : wa_data.cart.content[i].productID,
name : wa_data.cart.content[i].name,
// ....
}
}
But we don't usually add things to an Array by its index. We just push things into it :
for(i=0; i < tc_vars.cart.content.length; i++) {
tc_vars.cart.content.push({
productID : wa_data.cart.content[i].productID,
name : wa_data.cart.content[i].name,
// ....
});
}
This being said, it looks like all you're doing here is copying (not instanciating) wa_data.cart.content into tc_vars.cart.content. So you can completely forget my answer and replace your whole for loop with Gurvinder's answer (+1'd him):
tc_vars.cart.content = JSON.parse(JSON.stringify(wa_data.cart.content));
Unless wa_data already have objects repeated at all the index, following code should work
tc_vars.cart.content = JSON.parse(JSON.stringify(wa_data.cart.content));
You can use an object literal:
tc_vars.cart.content[i] = {
productID: wa_data.cart.content[i].productID,
name: wa_data.cart.content[i].name,
quantity: wa_data.cart.content[i].quantity,
promoCode: wa_data.cart.content[i].promoCode,
offerID: wa_data.cart.content[i].offerID,
salesType: wa_data.cart.content[i].salesType,
platform: wa_data.cart.content[i].platform,
unitSalePrice: wa_data.cart.content[i].unitSalePrice,
salePrice: wa_data.cart.content[i].salePrice,
stockAvailability: wa_data.cart.content[i].stockAvailability,
lineItemTotalAmountDiscounted: wa_data.cart.content[i].lineItemTotalAmountDiscounted,
lineItemTotalAmountNotDiscounted: wa_data.cart.content[i].lineItemTotalAmountNotDiscounted,
lineItemTaxAmount: wa_data.cart.content[i].lineItemTaxAmount,
lineItemDiscountedAmount: wa_data.cart.content[i].lineItemDiscountedAmount,
lineItemShippingCost: wa_data.cart.content[i].lineItemShippingCost,
crossSell: wa_data.cart.content[i].crossSell,
upSell: wa_data.cart.content[i].upSell
}

angularjs - copy common properties from one object to another

I have a controller like this:
CheckoutController = function() {
$scope.Profile = {
firstname : 'Ruchir',
middlename : 'Shakun',
lastname : 'Gupta',
email : 'ruchir#example.com',
cellphone : '9876543210'
}
$scope.BillingDetails = {
firstname : undefined,
middlename : undefined,
lastname : undefined,
addressline : undefined,
city : undefined,
zipcode : undefined
}
$scope.update = function() {
// I want to write some awesome code here as explained below
}
}
Now, in the $scope.update function; I want to write something that should copy 'only common properties' i.e. firstname, middlename, and lastname from $scope.Profile to $scope.BillingDetails.
I tried angular.copy and angular.extend but,
angular.extend merges $scope.BillingDetails and $scope.Profile.
So I get email and cellphone properties in $scope.BillingDetails as
well -- what I don't want.
angular.copy overwrites
$scope.BillingDetails and I lose addressline, city and
zipcode from $scope.BillingDetails -- what I don't want.
What I want my update function to do is it should make $scope.BillingDetails equal to below object:
{
firstname : 'Ruchir',
middlename : 'Shakun',
lastname : 'Gupta',
addressline : undefined,
city : undefined,
zipcode : undefined
}
This scenario is just an example. To shorten the length of my question, I have mentioned 5-6 properties only. In fact, I have to deal with more than 20 properties and all are dynamic. So it won't work for me by copying one-by-one properties firstname, middlename and lastname from Profile to BillingDetails.
What can I do?
You may have luck with something like this:
$scope.update = function() {
_update($scope.Profile, $scope.BillingDetails);
}
function _update(srcObj, destObj) {
for (var key in destObj) {
if(destObj.hasOwnProperty(key) && srcObj.hasOwnProperty(key)) {
destObj[key] = srcObj[key];
}
}
}
plunker
Simple. Just assign them like this:
$scope.update = function() {
$scope.BillingDetails.firstname = $scope.Profile.firstname;
$scope.BillingDetails.middlename = $scope.Profile.middlename;
$scope.BillingDetails.lastname = $scope.Profile.lastname;
}
I really can't think of a more straightforward method of copying a couple of properties from one object to another.
Since you need to copy more than 3 properties, you could try this:
$scope.update = function() {
// Add the properties you want to copy to this array.
var properties = ['firstname', 'middlename', 'lastname'];
for(var i = 0; i < properties.length; i++){
$scope.BillingDetails[properties[i]] = $scope.Profile[properties[i]];
}
}
Or, pass the array as a parameter:
$scope.update = function(properties) {
for(var i = 0; i < properties.length; i++){
$scope.BillingDetails[properties[i]] = $scope.Profile[properties[i]];
}
}
$scope.update(['firstname', 'middlename', 'lastname']);
In fact, you try to update BillingDetails with values of Profile, for properties they both have in common right?
If you can change the default values of BillingDetails with null instead of undefined, you can try this code:
$scope.BillingDetails = {
firstname : null,
middlename : null,
lastname : null,
addressline : null,
city : null,
zipcode : null
}
$scope.update = function() {
for(var key in $scope.Profile) {
if(typeof $scope.BillingDetails[key] !== 'undefined') {
$scope.BillingDetails[key] = $scope.Profile[key];
}
}
}

Look for a value for a given key in JSON and change the value using javascript

I am looking to write a function which can look up a value based on a key and replace that value with another. The key is a tree from the start node of JSON. Here is the example.
var myData = {
name : 'Dan',
address: {
city : 'Santa Clara',
details : {
'prevhouse' : ''
}
}
}
Input to the function is a key tree. For eg, myData-address-details-prevhouse
When I pass this key with a new value, say 'Texas', the prevhouse value will get changed to the new value I am sending.
and new JSON will be
var myData = {
name : 'Dan',
address: {
city : 'Santa Clara',
details : {
'prevhouse' : 'Texas'
}
}
}
Here is what I wrote so far
var tree = key.split("-");
now the tree variable contains ["myData","address", "details","prevhouse"]
I know that we can access the object using myData[tree[0]][tree[1]][tree[2]], but somehow not able to get it dynamic from parsed value.
how do we generate this dynamically since the length of the depth is not known till runtime.
Hope to get a help.
try with this code:
var myData = {
name: 'Dan',
address: {
city: 'Santa Clara',
details: {
prevhouse: ''
}
}
};
function setAttribute(obj, key, value) {
var i = 1,
attrs = key.split('-'),
max = attrs.length - 1;
for (; i < max; i++) {
attr = attrs[i];
obj = obj[attr];
}
obj[attrs[max]] = value;
console.log('myData=', myData);
}
setAttribute(myData, "myData-address-details-prevhouse", "Texas");
here a working jsfiddle demo; see the console for the result
You should be able to iterate through each key because your JSON is just a JS object. So go through each key, check if it's defined, if it is, use that object for your next check. That'll get you where you want to go. Keep in mind you'll be setting the last key to your value.
basic psuedo-code without dealing with setting:
obj = data;
for (key in keys) {
obj = obj[key]
}
Something like this would do:
function update(node, path, value) {
path = path.split('-');
do {
node = node[path.splice(0, 1)];
} while(path.length > 1);
node[path[0]] = value;
}
Given that myData is the object, I think you should be using myData[tree[1]][tree[2]][tree[3]] and throwing away the first item in the array.
Something like this should work recursively (untested)
function updateValue(obj, key, value)
{
var keys = key.split('-');
updateObjectValue(obj, keys.shift(), value);
}
function updateObjectValue(obj, keyArray, value)
{
if (keyArray.length == 1) {
obj[keyArray[0]] = value;
}
else if (keyArray.length > 1) {
updateObject(obj[keyArray[0]], keyArray.shift(), value);
}
}

Categories