I am trying to append an array of objects(new) to the local storage which already has some array of objects(previous) built in. Specifically, I want to merge these 2 arrays (previous and new) in the local storage.
Have tried the below code :
function appendToStorage(name, data)
{
var old = localStorage.getItem(name);
if(old === null)
old = "";
localStorage.setItem(name, old.concat(data));
}
appendToStorage('ObjAry', JSON.stringify(objectIdArray));
And this is the output that I am getting :
["IrGszUBa0F","l366vn6mPa","2qn7JUoRwg","s2fZa0mXnb","WIaXLwmXRa"]["ZKHtnHoHgH","rtbI1sDfPm","U1eVDi9bNM","tUGNCl6hNl","lkq6tswVsZ"]
All I want is that, the second array should append to the first array so the output becomes :
["IrGszUBa0F","l366vn6mPa","2qn7JUoRwg","s2fZa0mXnb","WIaXLwmXRa","ZKHtnHoHgH","rtbI1sDfPm","U1eVDi9bNM","tUGNCl6hNl","lkq6tswVsZ"]
Can anyone guide me on what I am doing wrong ?
You are pretty close, there are just three small mistakes:
You are stringifying the array before concatenating it (so you are attaching a string to an array).
The default value for old is a string, which should probably be an array?
In order to use the array from localstorage, you need to parse it again using JSON.parse.
The resulting code would then be:
function appendToStorage(name, data)
{
var old = localStorage.getItem(name);
if(old == null) {
old = [];
} else {
old = JSON.parse(old);
}
localStorage.setItem(name, JSON.stringify(old.concat(data)));
}
appendToStorage('ObjAry', objectIdArray);
If your local storage entry could contain other values as well, you could add a try ... catch block to your code to make sure that JSON.parse doesn't blow up if it fails to parse the value:
function appendToStorage(name, data)
{
var old = localStorage.getItem(name);
try {
old = JSON.parse(old);
} catch(e) {
old = [];
}
localStorage.setItem(name, JSON.stringify(old.concat(data)));
}
appendToStorage('ObjAry', objectIdArray);
You're concatenating the whole object returning from
JSON.stringify(objectIdArray)
Try
appendToStorage('ObjAry', objectIdArray);
LocalStorage stores a string values by a string key. To store arrays/objects as string we serialize them into JSON. So you need to parse JSON after getItem, merge parsed value with new portion of data, convert merged object to JSON and pass it to setItem.
function appendToStorage(name, data) {
var old = localStorage.getItem(name) || '[]';
var oldObject = JSON.parse(old) || [];
var merged = oldObject.concat(data);
localStorage.setItem(name, JSON.stringify(merged));
}
appendToStorage('ObjAry', objectIdArray);
Related
In localStorage I set previously various objects which are stored in this format
console.log(localStorage)
// Outputs the storage this way, it may contain different objects (randid for example)
Storage {
876346545: "{"ABA":"876346545","ABB":"37578533567","ABC":"AE123456","ABD":"1234567890123456"}",
randid: "fhjidc06om230j2r367gh1i5iec",
353446545: "{"ABA":"353446545","ΑBB":"12343212341","ΑBC":"CC949590","ABD":"5943949562340543"}",
length: 3
}
I try to find if a specific pair exists in this array of objects for example
//Pseudo-code
if anyofObject in localStorage contains pair ("ABA":"876346545") return true or false
My code which does not do what I expect
var data = JSON.parse(localStorage) // I tried and without Parsing it as JSON
for (var key in data) {
if (data[key].ABA = "876346545") {
var m = data[key];
console.log("Found: ", m)
break;
}
}
If you want to go through all the values stores in your local storage. You will have to do the following:
Get all the values using Object.values, and start filtering them one by one
Try to parse them as JSON object, some will fail because they are not JSON object, those are not of our interest
Those that parsed successfully, you need to check the key and see if the value for that key matches properly as in matches in type as well as value. Return true so that we can be done with our search and the value is stored in a variable.
Also, please note when you are comparing values use === and not = in your if statements as you have shown in your question. Also, take care types when comparing. You are setting ABA as a number but you are trying to compare with a string.
userData = {
"ABA": 876346545,
"ABB": 37578533567,
"ABC": "AE123456",
"ABD": 1234567890123456
};
localStorage.setItem("Some Random Key", JSON.stringify(userData));
// This will find the object and store it in foundObject variable
const foundObject = Object.values(localStorage).find(v => {
try {
return JSON.parse(v).ABA === 876346545;
} catch(e) {
return false;
};
});
console.log(JSON.stringify(foundObject));
// This will find the object and store true if found, false if not found in hasObject variable
const hasObject = Object.values(localStorage).some(v => {
try {
return JSON.parse(v).ABA === 876346545;
} catch(e) {
return false;
};
});
console.log(JSON.stringify(hasObject));
You have to parse the stored object in the localStorage, because it is stored as a string.
for (var key in localStorage) {
try {
const value = JSON.parse(localStorage[key]);
if (value.ABA == "876346545") {
var m = localStorage[key];
console.log("Found: ", m)
break;
}
} catch(e) {
continue;
}
}
I must be missing something here, but the following code (Fiddle) returns an empty string:
var test = new Array();
test['a'] = 'test';
test['b'] = 'test b';
var json = JSON.stringify(test);
alert(json);
What is the correct way of JSON'ing this array?
JavaScript arrays are designed to hold data with numeric indexes. You can add named properties to them because an array is a type of object (and this can be useful when you want to store metadata about an array which holds normal, ordered, numerically indexed data), but that isn't what they are designed for.
The JSON array data type cannot have named keys on an array.
When you pass a JavaScript array to JSON.stringify the named properties will be ignored.
If you want named properties, use an Object, not an Array.
const test = {}; // Object
test.a = 'test';
test.b = []; // Array
test.b.push('item');
test.b.push('item2');
test.b.push('item3');
test.b.item4 = "A value"; // Ignored by JSON.stringify
const json = JSON.stringify(test);
console.log(json);
Nice explanation and example above. I found this (JSON.stringify() array bizarreness with Prototype.js) to complete the answer. Some sites implements its own toJSON with JSONFilters, so delete it.
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
it works fine and the output of the test:
console.log(json);
Result:
"{"a":"test","b":["item","item2","item3"]}"
I posted a fix for this here
You can use this function to modify JSON.stringify to encode arrays, just post it near the beginning of your script (check the link above for more detail):
// Upgrade for JSON.stringify, updated to allow arrays
(function(){
// Convert array to object
var convArrToObj = function(array){
var thisEleObj = new Object();
if(typeof array == "object"){
for(var i in array){
var thisEle = convArrToObj(array[i]);
thisEleObj[i] = thisEle;
}
}else {
thisEleObj = array;
}
return thisEleObj;
};
var oldJSONStringify = JSON.stringify;
JSON.stringify = function(input){
if(oldJSONStringify(input) == '[]')
return oldJSONStringify(convArrToObj(input));
else
return oldJSONStringify(input);
};
})();
Another approach is the JSON.stringify() replacer function param. You can pass a 2nd arg to JSON.stringify() that has special handling for empty arrays as shown below.
const arr = new Array();
arr.answer = 42;
// {"hello":"world","arr":{"answer":42}}
JSON.stringify({ hello: 'world', arr }, function replacer(key, value) {
if (Array.isArray(value) && value.length === 0) {
return { ...value }; // Converts empty array with string properties into a POJO
}
return value;
});
Alternatively you can use like this
var test = new Array();
test[0]={};
test[0]['a'] = 'test';
test[1]={};
test[1]['b'] = 'test b';
var json = JSON.stringify(test);
alert(json);
Like this you JSON-ing a array.
Json has to have key-value pairs. Tho you can still have an array as the value part. Thus add a "key" of your chousing:
var json = JSON.stringify({whatver: test});
I'm writing a simple game. It's based on a field that is 32x32 squares. Each square can contain a game element. When an element appears/moves/changes, the server sends a json object containing a 2d array that resembles the whole game field with all it's elements via websockets to all clients.
That is quite a lot of data when sent to multiple clients, multiple times per second.
So I was wondering how I could reduce that.
I thought it would help to remove everything from the big array that the client already received anyways. But if you have something like this:
var gameField = [];
gameField[6][10] = "player1";
console.log(JSON.stringify(gameField));
it converts to:
[null,null,null,null,null,null,[null,null,null,null,null,null,null,null,null,null,"player1"]]
That's really unnecessary overhead.
Couldn't it generate something like this instead?
{"6": {"10":"player1"}}
I'm using node.js for the game server btw.
So how do I reduce the data? Is there maybe a special JSON.stringify method?
You can use an object instead of an array.
var gameField = {};
gameField[6] = gameField[6] || {};
gameField[6][10] = "player1";
console.log(JSON.stringify(gameField));
You can customize the json parsing, by using a replacer function. However the parsing reviver would depend on how the inner objects are recognized. For example, if the inner objects are strings as in the example:
var gameField = [];
gameField[6] = [];
gameField[6][10] = "player1";
function replacer(key,val){
if(val instanceof Array){
return val.reduce((o,el,i)=>{
if(el)o[i] = el;
return o;
},{});
}
return val;
}
var json = JSON.stringify(gameField, replacer);
console.log("JSON: ", json);
function reviver(key,val){
if(typeof val === 'string')
return val;
return Object.keys(val).reduce((arr,k) =>{
arr[k] = val[k];
return arr;
}, [] );
}
gameField = JSON.parse(json, reviver);
console.log("Parsed: ", gameField);
If you want to remove null from the array, use underscore library.
npm install underscore --save
in your file where the related code is there,
var _ = require('underscore');
To remove null just pass
result = _.filter(result, function(item){
return item!= null;
});
result will be an array without any null.
I'm trying to save some data in localstorage. My script looks like this:
localStorage.clear(); //only for testing
if(typeof localStorage.akten == "undefined") {
localStorage.akten = new Array();
}
var nam = "alpha";
localStorage.akten[nam] = {
"beta": 12
};
localStorage["a_akte"] = nam;
But if I do console.log(localStorage); or console.log(localStorage.akten); akten is only an empty string? Why? With an normal object instead of localStorage it works well.
Suprisingly the devil is in the details. localStorage only stores strings. Encode your objects as JSON before depositing them there using for example JSON.stringify() and JSON.parse().
This is because localStorage is not an Object; it's an interface. You can only assign a String to it, and the best way to do so is with localStorage.setItem. If you want to be setting more complex data, you'll need to encode it as JSON first.
function localStore(key, obj) {
return window.localStorage.setItem(key, JSON.stringify(obj));
}
function localGet(key) {
return JSON.parse(window.localStorage.getItem(key));
}
localStore('foo', {bar: 'baz'});
localGet('foo'); // Object {bar: "baz"}
TL;DR version: I want to avoid adding duplicate Javascript objects to an array of similar objects, some of which might be really big. What's the best approach?
I have an application where I'm loading large amounts of JSON data into a Javascript data structure. While it's a bit more complex than this, assume that I'm loading JSON into an array of Javascript objects from a server through a series of AJAX requests, something like:
var myObjects = [];
function processObject(o) {
myObjects.push(o);
}
for (var x=0; x<1000; x++) {
$.getJSON('/new_object.json', processObject);
}
To complicate matters, the JSON:
is in an unknown schema
is of arbitrary length (probably not enormous, but could be in the 100-200 kb range)
might contain duplicates across different requests
My initial thought is to have an additional object to store a hash of each object (via JSON.stringify?) and check against it on each load, like this:
var myHashMap = {};
function processObject(o) {
var hash = JSON.stringify(o);
// is it in the hashmap?
if (!(myHashMap[hash])) {
myObjects.push(o);
// set the hashmap key for future checks
myHashMap[hash] = true;
}
// else ignore this object
}
but I'm worried about having property names in myHashMap that might be 200 kb in length. So my questions are:
Is there a better approach for this problem than the hashmap idea?
If not, is there a better way to make a hash function for a JSON object of arbitrary length and schema than JSON.stringify?
What are the possible issues with super-long property names in an object?
I'd suggest you create an MD5 hash of the JSON.stringify(o) and store that in your hashmap with a reference to your stored object as the data for the hash. And to make sure that there are no object key order differences in the JSON.stringify(), you have to create a copy of the object that orders the keys.
Then, when each new object comes in, you check it against the hash map. If you find a match in the hash map, then you compare the incoming object with the actual object that you've stored to see if they are truly duplicates (since there can be MD5 hash collisions). That way, you have a manageable hash table (with only MD5 hashes in it).
Here's code to create a canonical string representation of an object (including nested objects or objects within arrays) that handles object keys that might be in a different order if you just called JSON.stringify().
// Code to do a canonical JSON.stringify() that puts object properties
// in a consistent order
// Does not allow circular references (child containing reference to parent)
JSON.stringifyCanonical = function(obj) {
// compatible with either browser or node.js
var Set = typeof window === "object" ? window.Set : global.Set;
// poor man's Set polyfill
if (typeof Set !== "function") {
Set = function(s) {
if (s) {
this.data = s.data.slice();
} else {
this.data = [];
}
};
Set.prototype = {
add: function(item) {
this.data.push(item);
},
has: function(item) {
return this.data.indexOf(item) !== -1;
}
};
}
function orderKeys(obj, parents) {
if (typeof obj !== "object") {
throw new Error("orderKeys() expects object type");
}
var set = new Set(parents);
if (set.has(obj)) {
throw new Error("circular object in stringifyCanonical()");
}
set.add(obj);
var tempObj, item, i;
if (Array.isArray(obj)) {
// no need to re-order an array
// but need to check it for embedded objects that need to be ordered
tempObj = [];
for (i = 0; i < obj.length; i++) {
item = obj[i];
if (typeof item === "object") {
tempObj[i] = orderKeys(item, set);
} else {
tempObj[i] = item;
}
}
} else {
tempObj = {};
// get keys, sort them and build new object
Object.keys(obj).sort().forEach(function(item) {
if (typeof obj[item] === "object") {
tempObj[item] = orderKeys(obj[item], set);
} else {
tempObj[item] = obj[item];
}
});
}
return tempObj;
}
return JSON.stringify(orderKeys(obj));
}
And, the algorithm
var myHashMap = {};
function processObject(o) {
var stringifiedCandidate = JSON.stringifyCanonical(o);
var hash = CreateMD5(stringifiedCandidate);
var list = [], found = false;
// is it in the hashmap?
if (!myHashMap[hash] {
// not in the hash table, so it's a unique object
myObjects.push(o);
list.push(myObjects.length - 1); // put a reference to the object with this hash value in the list
myHashMap[hash] = list; // store the list in the hash table for future comparisons
} else {
// the hash does exist in the hash table, check for an exact object match to see if it's really a duplicate
list = myHashMap[hash]; // get the list of other object indexes with this hash value
// loop through the list
for (var i = 0; i < list.length; i++) {
if (stringifiedCandidate === JSON.stringifyCanonical(myObjects[list[i]])) {
found = true; // found an exact object match
break;
}
}
// if not found, it's not an exact duplicate, even though there was a hash match
if (!found) {
myObjects.push(o);
myHashMap[hash].push(myObjects.length - 1);
}
}
}
Test case for jsonStringifyCanonical() is here: https://jsfiddle.net/jfriend00/zfrtpqcL/
Maybe. For example if You know what kind object goes by You could write better indexing and searching system than JS objects' keys. But You could only do that with JavaScript and object keys are written in C...
Must Your hashing be lossless or not? If can than try to lose compression (MD5). I guessing You will lose some speed and gain some memory. By the way, do JSON.stringify(o) guarantees same key ordering. Because {foo: 1, bar: 2} and {bar: 2, foo: 1} is equal as objects, but not as strings.
Cost memory
One possible optimization:
Instead of using getJSON use $.get and pass "text" as dataType param. Than You can use result as Your hash and convert to object afterwards.
Actually by writing last sentence I though about another solution:
Collect all results with $.get into array
Sort it with buildin (c speed) Array.sort
Now You can easily spot and remove duplicates with one for
Again different JSON strings can make same JavaScript object.