Push element into JSON object - javascript

I want to go through a JSON, if a certain condition applies then push some extra elements in that index.
I have this JS code:
$scope.addRoleToUser = function() {
var userid = $scope.selectedUser;
var tmpdata = [];
var index = 0;
//alert(userid);
angular.forEach($scope.users, function(data) {
if (data.id == $scope.selectedUser) {
tmpdata.push(data,{"roles":[{"id":"00","name":"newrole"}]});
}
else {
tmpdata.push(data);
}
index++;
});
$scope.users = tmpdata;
};
This is my initial JSON element:
$scope.users = [
{"id":"0","name":"User1","roles":[{}]},
{"id":"1","name":"User2","roles":[{}]},
]
I'm trying to get it to look like this after the function runs:
$scope.users = [
{"id":"0","name":"User1","roles":[{"id":"00","name":"newrole"}]},
{"id":"1","name":"User2","roles":[{}]},
]
But instead I'm getting this:
[{"id":"0","name":"User1","roles":[{}]},{"roles":[{"id":"00","name":"newrole"}]},{"id":"1","name":"User2","roles":[{}]}]

Just replace this inside your function
if (data.id == $scope.selectedUser) {
data.roles = [{"id":"00","name":"newrole"}];
}
Or, if you know that roles is not empty, you can do:
if (data.id == $scope.selectedUser) {
data.roles.push({"id":"00","name":"newrole"});
}
And after this line you can add your data to tmpdata!
That snippet now will look like this:
if (data.id == $scope.selectedUser) {
data.roles = [{"id":"00","name":"newrole"}]}); //or the other one
}
tmpdata.push(data);

Inside the forEach() callback you're just working with objects and as such, you can modify them directly inside the callback:
angular.forEach($scope.users, function(data) {
if (data.id == $scope.selectedUser) {
data.roles = [{"id":"00","name":"newrole"}];
}
});
Similarly you could modify almost anything of each entry by manipulating the respective data object.
Example Fiddle

The Array.prototype.push method is variadic: (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push).
When you call tmpdata.push(a,b,c), you are in essence appending the array [a,b,c] to tmpdata.
You can also decompose the problem with something like:
$scope.addRoleToUser = function() {
var thisUserid = $scope.selectedUser;
function addRolesFor(user) {
if (user.id === thisUserId){ user.roles = [{"id":"00","name":"newrole"}] };
return user;
}
retrun $scope.users.map(addRoles);
}
Please use the map function that is appropriate for your environment (like _.map), because the Array.prototype.map method is not supported by all browsers.

Related

Convert Javascript array back into JQuery-type object

For the code below, I wanted to make the _formsOk function work for both Javascript arrays and "JQuery objects". In function1(), I tried to create a Javascript array with all DOM elements except those that have a parent element with id="objectTypesContainer". Basically, function1() filters out the DOM elements I don't want before calling _formsOk() function, which does the actual form validation.
function1() {
var allForms = $('form:not(.vv_hidden)', this.selectMarketsContainer);
var nonObjectTypeForms = [];
allForms.each(function () {
if ($(this).parent().attr("id") !== "objectTypesContainer"){
nonObjectTypeForms.push($(this)[0]);
}
});
return this._formsOk(nonObjectTypeForms);
},
_formsOk: function($forms) {
var formOk = true;
console.log(typeof $forms)
$forms.each(function () { // This line fails
var validator = $(this).validate(DEFAULT_VALIDATION_OPTIONS);
if (!(validator && validator.form())) {
formOk = false;
}
});
return formOk;
},
However, I realized that because nonObjectTypeForms is now a JS Array rather than a "JQuery Object", the line marked (// This line fails) now fails.
The original code looked like this:
function1() {
var allForms = $('form:not(.vv_hidden)', this.selectMarketsContainer); // This is a "JQuery object", so no error occurs
return this._formsOk(allForms);
},
_formsOk: function($forms) {
var formOk = true;
console.log(typeof $forms)
$forms.each(function () { // This line fails
var validator = $(this).validate(DEFAULT_VALIDATION_OPTIONS);
if (!(validator && validator.form())) {
formOk = false;
}
});
return formOk;
},
Is there a way I can convert a JS array into a JQuery object ? I don't want to change _formsOk function definition just yet.
Instead of putting all elements in a new array, just use .filter() from the jQuery object.
allForms.filter(function () {
return $(this).parent().attr("id") !== "objectTypesContainer")
});
This will remove all the items you don't need in your selection and now allForms will only have the wanted elements.

Can't get array.splice to work in Angular

I have the object words and a checkbox which should hide a specific element from this object, but I cannot get it work.
<body ng-controller="ArrController">
<input type="checkbox" ng-model="hide"> {{kc}}
{{words}}
</body>
The ArrController:
app.controller('ArrController', function ($scope, $http) {
$scope.hide = false;
$http.get('array.json').success(function(data) {
var keyword = 'lol';
$scope.words = data.unsorted_arr;
$scope.$watch('hide', function () {
if ($scope.hide == true) {
var remove = function() {
$scope.words.splice(keyword, 1);
}
$scope.kc = 'hidden';
} else {
$scope.kc = 'not hidden';
$scope.words = data.unsorted_arr;
}
});
});
});
The file array.json contains data for words:
{"unsorted_arr":{"gonna":3,"lol":114,"wouldn":2,"know":6,"lowkey":2,"man":5}}
The kc modifies according to the checkbox status, but the words stays the same.
Where am I wrong?
Splice is for removing something in an array, and it takes in two integers as parameters.
Since you have an object, just use delete:
delete $scope.words[keyword];
By doing, $scope.words = data.unsorted_arr, the two variables refer to the same object so deleting something from $scope.words will delete it from data.unsorted_arr.
Keep a reference to it so you can repopulate it later:
var word = $scope.words[keyword];
...
delete $scope.words[keyword];
...
$scope.words[keyword] = word;
You are confusing Indexed arrays with associative arrays,
Array.splice is a method of Indexed Arrays,
you have a simple Javascript Object (associative array)...
on POJO you can use the delete operator or a simply reassignment to undefined:
var a = { foo: 'baz' };
delete a['foo'];
var b = ['foo', 'baz'];
b.splice(0, 1)
In your example you are defining a function for removing the element, but the function is never being called.
var remove = function() {
$scope.words.splice(keyword, 1);
}
you may need to your logic to remove the function (as it doesn't seem to be needed) and replace the of use splice with the delete statement:
$http.get('array.json').success(function(data) {
var keyword = 'lol';
$scope.words = data.unsorted_arr;
$scope.$watch('hide', function () {
if ($scope.hide == true) {
delete $scope.words[keyword];
$scope.kc = 'hidden';
} else {
$scope.kc = 'not hidden';
$scope.words = data.unsorted_arr;
}
});
});

how to use $scope.$watchCollection insted of angular.forEach()

I have this block of code when user do delete or undelete it will update deleted property on model. my problem I'm using for angular.forEach() just want to see if it possible to change angular.forEach to use $scope.$watchCollection() ?
$scope.deleteForm = function (form) {
var result = confirm('Want to delete the form ?');
if (result){
var splitDeleteHref = form.deleteHref.split('/');
var formName = splitDeleteHref[1];
var formId = splitDeleteHref[2];
homeService.deleteForm(formName, formId);
$scope.$watchCollection('forms',)
//angular.forEach($scope.forms, function(value, key) {
// if (value.recordId === form.recordId) {
// $scope.forms[key].deleted = true;
// }
//});
}
};
$scope.undeleteForm = function (form) {
var result = confirm('Want to undelete the form ?');
if (result) {
homeService.undeleteForm(form).then(function () {
//angular.forEach($scope.forms, function (value, key) {
// if (value.recordId === form.recordId) {
// $scope.forms[key].deleted = false;
// }
//});
});
}
};
$scope.$watch and .$watchCollection are for watching for changes to an object or a collection of objects. These methods cannot be used to iterate over an array, which is what angular.forEach is for. Your usage of .forEach appears to be correct, so I recommend leaving it as is.

Array that can store only one type of object?

Is it possible to create an array that will only allow objects of a certain to be stored in it? Is there a method that adds an element to the array I can override?
Yes you can, just override the push array of the array (let's say all you want to store are numbers than do the following:
var myArr = [];
myArr.push = function(){
for(var arg of arguments) {
if(arg.constructor == Number) Array.prototype.push.call(this, arg);
}
}
Simply change Number to whatever constructor you want to match. Also I would probably add and else statement or something, to throw an error if that's what you want.
UPDATE:
Using Object.observe (currently only available in chrome):
var myArr = [];
Array.observe(myArr, function(changes) {
for(var change of changes) {
if(change.type == "update") {
if(myArr[change.name].constructor !== Number) myArr.splice(change.name, 1);
} else if(change.type == 'splice') {
if(change.addedCount > 0) {
if(myArr[change.index].constructor !== Number) myArr.splice(change.index, 1);
}
}
}
});
Now in ES6 there are proxies which you should be able to do the following:
var myArr = new Proxy([], {
set(obj, prop, value) {
if(value.constructor !== Number) {
obj.splice(prop, 1);
}
//I belive thats it, there's probably more to it, yet because I don't use firefox or IE Technical preview I can't really tell you.
}
});
Not directly. But you can hide the array in a closure and only provide your custom API to access it:
var myArray = (function() {
var array = [];
return {
set: function(index, value) {
/* Check if value is allowed */
array[index] = value;
},
get: function(index) {
return array[index];
}
};
})();
Use it like
myArray.set(123, 'abc');
myArray.get(123); // 'abc' (assuming it was allowed)

Call function in parent's parent from inside jquery's .each

I've created a 'class' in javascript called QuoteProductService(), see below.
I've added two functions to the prototype and now, I'm trying to call one of the functions (getQuoteProductFromArray) from within a jquery $.each inside the other function (getFakeQuoteProducts). This doesn't work. I've tried adding 'this.', but this also does not work, because 'this' inside the .each refers to the current element in the loop.
How should I do this ?
function QuoteProductService() {
}
QuoteProductService.prototype.getQuoteProductFromArray = function(quoteproductarray, quoteproductid){
var founditem=null;
// do stuff
return founditem;
}
QuoteProductService.prototype.getFakeQuoteProducts = function(){
// do something to fill the mappedQuoteProducts array
$.each(mappedQuoteProducts, function (index, quoteproduct) {
if (quoteproduct!=-null) {
if (quoteproduct.parentid != "") {
// this is where it goes wrong :
var parent = getQuoteProductFromArray(mappedQuoteProducts, quoteproduct.parentid);
if (parent != null) {
parent.attachChild(quoteproduct);
}
}
}
});
}
Save a reference to your QuoteProductService instance before calling each
QuoteProductService.prototype.getFakeQuoteProducts = function(){
var _this = this;
// do something to fill the mappedQuoteProducts array
$.each(mappedQuoteProducts, function (index, quoteproduct) {
if (quoteproduct!=-null) {
if (quoteproduct.parentid != "") {
// this is where it goes wrong :
var parent = _this.getQuoteProductFromFlatArray(mappedQuoteProducts, quoteproduct.parentid);
if (parent != null) {
parent.attachChild(quoteproduct);
}
}
}
});
}
Add var self = this; to the beginning of the getFakeQuoteProducts function. Then call getQuoteProductFromFlatArray like this: self.getQuoteProductFromFlatArray.
First of all you provided wrong method name - getQuoteProductFromFlatArray instead of getQuoteProductFromArray. Secondly in JS you must provide scope for instance methods.
Easiest way to achieve this is to store this reference into some other, private variable. See the example below.
function QuoteProductService() {
}
QuoteProductService.prototype.getQuoteProductFromArray = function(quoteproductarray, quoteproductid){
var founditem=null;
// do stuff
return founditem;
}
QuoteProductService.prototype.getFakeQuoteProducts = function(){
var me = this; // store this into me
// do something to fill the mappedQuoteProducts array
$.each(mappedQuoteProducts, function (index, quoteproduct) {
// this === me will return false
if (quoteproduct!=-null) {
if (quoteproduct.parentid != "") {
// this is where it goes wrong :
var parent = me.getQuoteProductFromArray(mappedQuoteProducts, quoteproduct.parentid);
if (parent != null) {
parent.attachChild(quoteproduct);
}
}
}
});
}

Categories