Removing json item causes error - javascript

I have a simple button that removes an item from a json object. This is currently working fine. The issue I have is that once it's clicked once it doesn't work again due to a js error. The error is reporting that an item is null.
I thought delete would remove the json item, not simply mark it as null.
See this JSFiddle
$("button").click(function() {
var jsonObj = $.parseJSON($('div').text());
var name;
if($(this).attr('id') == 'btn1') name = 'John2';
if($(this).attr('id') == 'btn2') name = 'Anna';
$.each(jsonObj, function(i, obj) {
if (obj.firstName == 'Anna') delete jsonObj[i];
});
$('div').text(JSON.stringify(jsonObj));
});
I need to get the json text from the div, remove an item from it, then save it as text back to the div. Any help would be appreciated.

you should be iterating over the .employees array element of the object
you can't delete an element from an array with delete - use .splice instead.
you should return false from the $.each callback once a match has been made, or you'll end up iterating over non-existent elements - you must always be careful when modifying the size of a collection whilst iterating over it.
See https://jsfiddle.net/alnitak/mjw4z7jL/1/

The reason is because you are removing items from the array while looping through the keys. When you remove an item, it will rearrange the other items depending on how the array is implemented internally, and you end up with a loop that doesn't iterate over the keys that you expect.
Use for loop instead of $.each or return false once you are inside the condition.
$("button").click(function() {
var jsonObj = $.parseJSON($('div').text());
var name;
if($(this).attr('id') == 'btn1') name = 'John2';
if($(this).attr('id') == 'btn2') name = 'Anna';
for(i=0; i < jsonObj.employees.length; i++){
if (jsonObj.employees[i].firstName == name){
jsonObj.employees.splice(i,1);
}
}
$('div').text(JSON.stringify(jsonObj));
});
https://jsfiddle.net/bipen/715qkhwo/4/
Remind you, this is not a proper solution and depends entirely on how your json object is. If there are two objects with same firstName, this might give you weird result. So make sure you add all of the needed condition before you delete it.

You were iterating through the root object, it has one single property, employees
You needed to loop through object.employees array
Far easier with native array filter function
Note: This will handle multiple Johns and Annas without issue
$("button").click(function() {
var jsonObj = $.parseJSON($('div').text());
var name;
if(this.id == 'btn1') name = 'John2';
else if(this.id == 'btn2') name = 'Anna';
else return;
jsonObj.employees = jsonObj.employees.filter(function(emp) {
return emp.firstName != name;
})
$('div').text(JSON.stringify(jsonObj));
});
https://jsfiddle.net/715qkhwo/5/

Related

Search multi-dimensional array before pushing to array, no duplicate data

I'm trying to build 'wishlist' functionality. When you click a button, it saves the name and link for the particular 'property' in an array in local storage. I will then output this on the page for the user.
So far it works, my only problem is that it will always insert the property name and link, even if it's already present in the array. I need to create a check, to see if it's already there, and only push it if it can't be found.
Here's a JSFiddle that works. Click the "save" buttons and check localStorage and you will see that the data is added. But click the same button again and you'll see it's added, again.
https://jsfiddle.net/g9kkx3fh/3/
Here's the basic code. It grabs the property name and link from the closest clicked button, it pulls data back from the array in localStorage, then uses .push to add the new data, then re-stringifys it and adds it back to localStorage.
var name = $(this).closest('.text-long').find('.intro-text h4').text();
var permalink = $(this).closest('.text-long').find('.button-slot a').attr('href');
var property = [name, permalink]; // create an array of the name + permalink
var wishlist = JSON.parse(localStorage.getItem("wishlist")); // get the wishlist from storage
wishlist.push(property); // append the new array into the wishlist from storage
localStorage.setItem('wishlist', JSON.stringify(wishlist)); // put the wishlist back in storage
I knew I needed to iterate over the array, being multi-dimensional, and look for the same name var. So I started with this:
for (var i = 0; i < wishlist.length; i++) {
var isPresent = ($.inArray(name, wishlist[i]));
}
if (isPresent == -1){
wishlist.push(property);
localStorage.setItem('wishlist', JSON.stringify(property));
}
Here's the problem with this. If the localStorage var wishlist is empty, then its length is 0. Therefore the for loop never works, because i < wishlist.length is never true, because wishlist is always 0.
So how do I fix this? I'm never able to add anything to the array, because I can never get the value of isPresent, because my for loop never works.
Here's a JSFiddle for the broken code, but with the for loop and if statement added:
https://jsfiddle.net/bdxa0sgz/1/
I've also tried the following:
if (isPresent == -1 || wishlist.length == 0){
...
}
So that if the wishlist is empty, it'll still run. However this seems to jumble together the name data and overwrite the array. I'm very confused.
https://jsfiddle.net/bdxa0sgz/5/
Before you push the property array to the wishlist array you have to check if wishlist contains it right. So you might simply do it like this. Let's invent a generic Array.prototype.compare() method. However since this is going to be a generic method it also takes care of the possibility of array elements being objects. So we have an Object.prototype.compare() too (in this case we don't need it but it's good to know) So here is the code;
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
Array.prototype.compare = function(a){
return this.every((e,i) => typeof a[i] === "object" ? a[i].compare(e) : a[i] === e);
};
var button1 = ["29 Melton Road32 York Road","http://localhost:8888/test-properties/properties/29-melton-road/"],
button2 = ["32 York Road","http://localhost:8888/test-properties/properties/32-york-road/"],
wishlist = [],
push2wl = btn => wishlist.some(a => a.compare(btn)) ? wishlist : wishlist.concat([btn]); // if exists return wishlist untouched
wishlist = push2wl(button1);
wishlist = push2wl(button2);
wishlist = push2wl(button1); // this won't get inserted
wishlist = push2wl(button2); // this won't get inserted
console.log(wishlist);
I use arrow functions but you may replace them with their conventional counterparts if you want to see your code work on Safari 9 or IE.
Use Map (key, value) pair to store your properties so that you can check using
myMap.get(key)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
Here, have a look at this detailed article: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map
For your example,
var myMap = new Map();
$('body').on('click', '.icon.heart button', function(e) {
var name = $(this).closest('.text-long').find('.intro-text h4').text();
var permalink = $(this).closest('.text-long').find('.button-slot a').attr('href');
var property = [name, permalink];
console.log(property);
if (myMap.get(name) === null) {
var wishlist = JSON.parse(localStorage.getItem("wishlist"));
console.log('Wishlist from localStorage = ' + wishlist);
wishlist.push(property);
console.log(wishlist);
localStorage.setItem('wishlist', JSON.stringify(wishlist));
myMap.set(name, permalink);
}
});
You can also iterate through the map to get all the properties like this:
var mapIter = myMap[Symbol.iterator]();
console.log(mapIter.next().value);
I hope this works for you.

Add values from one array to object with specified key & index

Im using the following code,
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
aSelectedDataSet.push(fnCreateEnt(aProp, oData, oPushedObject));
});
This is aSelectedDataSet values
and this is the values of OData
What I need is that before I do the push is to fill the listTypeGroup & listTypeGroupDescription (with the red arrow ) with values that Are inside the oData -> ListTypeGroupAssigment -> result (listTypeGroup & listTypeGroupDescription) , The index is relevant since I want to add just the value of the index in each iteration (since this code is called inside outer loop and the index determine the current step of the loop) ,How it can be done nicely?
The result contain 100 entries (always) and the a selected data will have 100 entries at the end...
Update :)
Just to be clear In the pic I show the values which is hardcoded for this run but the values can be any values, we just need to find the match between the both objects values...
I mean to find a match between to_ListTypeGroupAssigment in both object (which in this case exist ) and if in oData there is result bigger then one entry start with the matching ...
UPDATE2 - when I try Dave code the following happen for each entry,
This happen in the Jquery.extend line...any idea how to overcome this?
The following hard-coded of Dave:-) work perfect but I need generic code which doesnt refer to specific field name
jQuery.each(aDataSet, function(index, oData) {
oPushedObject = {};
fnCreatePushedEntry(aProperties, oData, oPushedObject);
var result = oData.to_ListTypeGroupAssignment.results[index];
oPushedObject.to_ListTypeGroupAssignment = {
ListTypeGroup: result.ListTypeGroup,
ListTypeGroupDescription: result.ListTypeGroupDescription
};
aSelectedDataSet.push(oPushedObject);
});
Im stuck :(any idea how to proceed here ?what can be wrong with the extend ?
should I use something else ? Im new to jQuery...:)
I think that this happen(in Dave answer) because the oData[key] is contain the results and not the specified key (the keyValue = to_ListTypeGroupAssignment ) which is correct but we need the value inside the object result per index...
var needValuesForMatch = {
ListTypeGroup: 'undefined',
ListTypeGroupDescription: 'undefined',
}
//Just to show that oPushedObject can contain additional values just for simulation
var temp = {
test: 1
};
//------------------This object to_ListTypeGroupAssigment should be filled (in generic way :) ------
var oPushedObject = {
temp: temp,
to_ListTypeGroupAssignment: needValuesForMatch
};
oPushedObject is one instance in aSelectedDataSet
and after the matching I need to do the follwing:
aSelectedDataSet.push(oPushedObject);
Is this what you're after:
OPTION ONE - DEEP CLONE FROM oData TO aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
var objectToClone = oData[childObject]['results'][index];
if(objectToClone)
$.extend(true,currentObject[childObject],objectToClone);
}
});
Here is your data in a fiddle with the function applied: https://jsfiddle.net/hyz0s5fe/
OPTION TWO - DEEP CLONE FROM oData ONLY WHERE PROPERTY EXISTS IN aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
if(typeof currentObject[childObject] !== 'object')
continue;
for(var grandChildObject in currentObject[childObject]) {
var objectToClone = oData[childObject]['results'][index][grandChildObject];
if(typeof objectToClone === 'object') {
$.extend(true,currentObject[childObject][grandChildObject],objectToClone);
} else {
currentObject[childObject][grandChildObject] = objectToClone;
}
}
}
Fiddle for option 2: https://jsfiddle.net/4rh6tt25/
If I am understanding you correctly this should just be a small change:
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
fnCreateEnt(aProp, oData, oPushObj);
//get all the properties of oData and clone into matching properties of oPushObj
Object.getOwnPropertyNames(oData).forEach(function(key) {
if (oPushObj.hasOwnProperty(key)) {
//oPushObj has a matching property, start creating destination object
oPushObj[key] = {};
var source = oData[key];
var destination = oPushObj[key];
//can safely assume we are copying an object. iterate through source properties
Object.getOwnPropertyNames(source).forEach(function(sourceKey) {
var sourceItem = source[sourceKey];
//handle property differently for arrays
if (Array.isArray(sourceItem)) {
//just copy the array item from the appropriate index
destination[sourceKey] = sourceItem.slice(index, index + 1);
} else {
//use jQuery to make a full clone of sourceItem
destination[sourceKey] = $.extend(true, {}, sourceItem);
}
});
}
});
aSelectedDataSet.push(oPushedObject);
});
It is unclear what exactly your fnCreateEnt() function returns though. I am assuming it is the populated oPushObj but it's not entirely clear from your question.

Add to array only once - leaving unique items

I program a function that give me all values of some input checkboxes and include them into an array.
Function:
$('#area_tbl .checkbox').each(function(){
/*for(var i = 0; i < test.length; i++){
if(test[i].PLZ === $(this).find('.area-checkbox').val()){
alert('Gleich');
}else{
alert('nicht gleich');
}
}*/
test.push({PLZ:$(this).find('.area-checkbox').val()});
});
My array looks like this:
[Object { PLZ="42799"}]
That's fine!
Now I include automatically more checkboxes with more values. After that my function is refreshing and I include the 'new' values.
Now my problem is that my array looks like this:
[Object { PLZ="42799"}, Object { PLZ="42799"}, Object { PLZ="51399"}]
You can see PLZ='42799' is twice.
I want to find the duplicate values and delete them from my array. I try it with the if clause in my function. But nothing works for me.
Assuming that value of each checkbox is unique, you need to reset the test value before running this each iterator
test = [];
$('#area_tbl .checkbox').each(function(){
test.push({PLZ:$(this).find('.area-checkbox').val()});
});
You could use a memory
// The memory will be a simple list with the already added elements. Firstly empty
memory = []
// we loop over ther checboxes
$('#area_tbl .checkbox').each(function(){
// we store the value
var v = $(this).find('.area-checkbox').val();
// If memory doesn't content the value... (its position is -1)
if(memory.indexOf(v) == -1){
// we store the object and we update the memory
test.push({PLZ:v});
memory.push(v);
}
});
You could use a temporary object and look up with accessing the property:
var object= {};
$('#area_tbl .checkbox').each(function() {
var v = $(this).find('.area-checkbox').val();
if (!object[v]) {
test.push({PLZ: v});
object[v] = true;
}
});

Remove an element from array with user input

I'm having an issue with removing items from a Javascript array. I am using a prompt, and when the user inputs a worker's ID number, I want that element to be removed from the array.
At the moment, my code will only remove the final element in the list.
This is my code:
var remove = function(){
var removeID = prompt("Enter ID of the worker you wish to remove: ");
var index = array.indexOf(removeID);
if(removeID == id){
array.splice(index, 1);
showArray();
}
else{
alert("id is not in the system");
}
}
You're mixing up array indices with elements. It should go like this:
var remove = function(){
var removeID = prompt("Enter ID of the worker you wish to remove: ");
var index = array.indexOf(removeID);
if(index > -1){ // HERE!!!
array.splice(index, 1);
showArray();
}
else{
alert("id is not in the system");
}
}
Two additional observations:
If IDs are numbers, then your call to array.indexOf() will always return -1. prompt() will always give you a string, not an integer; you should use parseInt() instead like this:
var removeID = parseInt(prompt("Enter ID of the worker you wish to remove: "));
Since IDs are meant to be unique, and depending on the context, it might make sense to store them inside an object rather than an array so that you don't risk having duplicate IDs.
I think you messed up your logic
if(removeID == id){
should probably be checking to see if the id exists in the array before removing it.
if(index !== -1){
and another guess since you did not give a runnable example:
var removeID = Number(prompt("Enter ID of the worker you wish to remove: "));
http://jsfiddle.net/dk4kb417/1/
This method should remove the index you want. It will ask for the element value. The first index of that value will be deleted:
var array = ['one','two','three',4,5,6,7,8,9,10];
var remove = function(removeID){
var index = array.indexOf(removeID);
if (index>-1) {
array.splice(index, 1);
}
}
remove(prompt("Enter ID of the worker you wish to remove: "));
console.log(array);

Remove an object from an from an array by it's id (angular)

I'm trying to remove an object from an array by it's key/value of ID. I would normally just splice by index, however the index might be changing quite a bit because multiple users will be manipulating and updating the object so I want to hook onto something more concrete - aka the id. So I have a bit of logic to check if it still exists and if so remove it by it's ID. However I can't seem to get the syntax quite correct. I Am using underscore.js, I don't know if it's easier with/without it but it's worth mentioning.
Here's what I have -
$scope.toggleSelection = function(typeId,name,index){
//check if exists in array
check = $scope.multipleTypes.some( function( el ) {
return el.name === name;
});
//checked on/off actions
if($scope.items[index].checked == false || $scope.items[index].checked == undefined ){
//IS cecked
if(check){
//already exists, do nothing
}else{
$scope.multipleTypes.push({id:typeId, name:name, checked: true});
}
}else{
//IS not checked
if(check){
var list = _.filter($scope.multipleTypes, function(element){
return element.id != typeId;
}
$scope.multipleTypes = list;
}else{
//is not there, do nothing
}
}
};
So if it does exist and is checked off, it gets pushed. If it does exist and is unchecked, I want to remove it from $scope.multipleTypes by it's ID. I think I Am doing this wrong, all I want to do is remove that one object that has the matching ID from $scope.multipleTypes. Would appreciate any help. Thanks for reading!
If you can use UnderScore Js, You can do it very easily.
Here is an Example:
var someArray= [{Employee:'ved',id:20},
{Employee:"ved",age:25},
{Employee:"p",age:2}];
var a = _.findWhere(someArray,{id:25});//searching Element in Array
var b= _.indexOf(someArray,a);// getting index.
someArray.splice(b,1);// removing.
I normally find the object by id, then splice it out. Note that angularjs adds other properties to the object .
e.g
$scope.items = [......]
var findItemByID = function(id, items){
angular.forEach(items, function(item){
if(item.id === id){
return item;
}
})
return null;
}
var removeItemByID = function(id, items){
var item = findItemByID(id);
if(item){
items.splice(items.indexOf(item), 1);
}
}
//you can now do removeItemByID(id, $scope.items);
//I have not tested this code and it may have syntax errors. hope you get the idea.
Josh

Categories