push() not working in javascript - javascript

Problem : Uncaught TypeError: Object #<Object> has no method 'push' in console.
Code in http://jsfiddle.net

Change storage item id(Cart) and try again, looks like previously stored item under "Cart" id is not json array as #dc5 suggested in comment section
UPD: Try this http://jsfiddle.net/vJkBQ/4/
HTML
<div id='cart'></div>
<input type="button" id="add" value="Add To Cart item 1" />
<input type="button" id="add2" value="Add To Cart item 2" />
Javascript
//TODO: move from globals
var storageName = 'myCART';
$(document).ready(function () {
var item = {
DepartmentID :333,
CategoryID:117,
BrandID:19,
BrandImage:" ",
BrandName:"General",
ID:711
};
var item2 = {
DepartmentID :123,
CategoryID:321,
BrandID:18,
BrandImage:" ",
BrandName:"Common",
ID:712
};
localStorage.clear(storageName);
$('#add').click(function(){
addToCart(item);
});
$('#add2').click(function(){
addToCart(item2);
});
});
function addToCart(item){
//by #slebetman
var items = JSON.parse(localStorage.getItem(storageName));
if (! (items instanceof Array) ) {
items = [];
}
var itemIndex = getItemIndexById(items, item.ID);
if(typeof(itemIndex) === 'number'){
items[itemIndex].quantity++;
}
else{
item.quantity = 1;
items.push(item);
}
localStorage.setItem(storageName, JSON.stringify(items));
console.log(localStorage.getItem(storageName));
}
//find search item index
function getItemIndexById(items, id){
for(var i = 0; i < items.length; i++){
if(items[i].ID == id){
return i;
}
}
return false;
}

The expression:
JSON.parse(localStorage.getItem(storageName))
is most likely not returning an array. In which case the statement:
var oldStorage = JSON.parse(localStorage.getItem(storageName)) || [];
is insufficient.
What you should do instead is something like this:
var oldStorage = JSON.parse(localStorage.getItem(storageName));
if (! (oldStorage instanceof Array) ) {
oldStorage = [];
}
This is a simple way to detect arrays. There are more advanced methods such as checking for the existance of .length etc. that detects arrays and array like objects as well as detect arrays in cases where the Array object have been overwritten or works across iframes.
Additional answer:
You've changed the code a lot but the problem is still the same. The line:
if (items != null) {
is not sufficient to check that items is an array. You should instead do:
if ( items instanceof Array ) {
to make sure that it really is an array.
Also, in the else block:
}else{
console.log('Cart is empty, preparing new cart array');
items.push(item);
The console.log message says that a new array is being prepared. However it lies as the code doesn't initialize a new array but uses the items variable as if it was an array. You should instead do this:
}else{
console.log('Cart is empty, preparing new cart array');
items = [];
items.push(item);
Warning:
However, after all that, please heed the the commenters to my question. If you wrote this whole thing from scratch than doing the things I advised would solve all your problems. But if what you're doing is modifying someone else's code then it's probable that Cart was stored in a different format than what you expected.
Please do console.log(localStorage['Cart']) before calling JSON.parse and post your results here. The problem is with your local storage on your browser and cannot usually be reproduced on other people's machines.

Does
JSON.parse(localStorage.getItem(storageName))
always return an array? If so, the issue is that not all browsers support the push method. You can add use this snippet to add it if it is missing:
if(!Array.prototype.push){
Array.prototype.push=function(x){
this[this.length]=x;
return true
}
};
This code is just a start, you can definitely improve it

You can use you can invoke Array.prototype.push() on an object using call().
JSFiddle
JavaScript
function appendToStorage(storageName, data){
var oldStorage = JSON.parse(localStorage.getItem(storageName)) || [];
Array.prototype.push.call(oldStorage, data);
localStorage.setItem(storageName,JSON.stringify(oldStorage));
}

JSON.parse returns an object or null. So here you don't know if you have an object or an Array.
JSON.parse(localStorage.getItem(storageName)) || [];
You can use this instead:
function appendToStorage(storageName, data){
var oldStorage = JSON.parse(localStorage.getItem(storageName)) || {};
if (oldStorage != null){
$(oldStorage).extend(data);
localStorage.setItem(storageName,JSON.stringify(oldStorage));
}
}
$.extend() will add your item to another JSON object. Info here

Your 'item' is not an array, and you cannot use a push method with it.

Object do not have 'push' method,you can do like this!
function appendToStorage( storageName, data ) {
var oldStorage = JSON.parse( localStorage.getItem( storageName ) ) || {},
push = Array.prototype.push;
push.call( oldStorage, data );
// oldStorage.data = data;
localStorage.setItem(storageName,JSON.stringify(oldStorage));
}

Related

Removing json item causes error

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/

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 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

Make use of a global array

I want to use the values that I get from a request, but the response object is a local variable (an array). Therefore I create this global array:
<script type="text/javascript">
var response = [];
as you see, right under the script opening tag, so it is global. Then in the function where I have the response I added this:
jsonResponse.forEach(function(element){
response[element.size] = element.id;
});
And then added this, with the purpose to make use of the values that I've got in my global var from the response object:
getIdOfProductBySize: function() {
var selectedIndex = document.getElementById('dropdown_options').value;
for (var key in response) {
if (key != selectedIndex) {
continue;
} else {
return response[key];
}
}
}
Doesn't work, so I started going step by step (of the order I add the new things) and I noticed that the script breaks after the 2nd thing that I add (where the forEach is).
Maybe I am not declaring the global variable correctly, or maybe I cannot access it this way, or maybe I don't assign the values to it in the correct way, I don't know, so I am asking if someone can give me a hint how to make use of all this working together?
Try this:
var response = {key1: value1};
var i = 2;
jsonResponse.forEach(function(entry) {
console.log(entry);
response["key"+i] = entry.id;
i++;
});
var index;
for (index = 0; index < response.length; ++index)
{
console.log(response[index]);
if(response["key"+index] !== selectedIndex)
continue;
else
return response["key"+index];
}
Looks like you're going to need a two dimensional array.
Looks to me like your "key" value is undefined.
before:
for (var key in response) {
try:
var k=response.whatever;
If that makes sense?
response[element.id] = element.size;
Try this one, i believe element.size returns the actual size of an element and is not what you want to use as index in an array.

Categories