array of unique objects to localStorage - javascript

i want to push an object to an array(last called objects) and store this array to localstorge. this array fills every call with new objects. If an objects still exist in the array, the older one will be replaced.
My code so far:
function pushToStorage(groupId, objectId, groupIcon, displayString) {
var objKey = "object_" + groupId + "_" + objectId;
var objects = storage.get("objects");
if (objects) {
console.log($objects);
} else {
objects = [];
}
var object = {
groupid: groupId,
objectid: objectId,
groupicon: groupIcon,
display: displayString
};
objects[objKey] = object;
console.log(objects);
storage.set("objects", objects);
}
i use this jquery plugin jstorage
im not an js pro and at the moment, only one object get stored correct.
So my questions:
How to store an array of objects to local storage, get it back, and add new objects to this array
How to manage that there is only one unique object in this array
How to limit the array by eg the 50 newest and kick the older ones
thx for any suggestions or snippets
EDIT: some people mark this as duplicate - but the linked answer is only a part of my. I read this before but my problem is to set/get an array with unique objects. i think it is more complex.

in your case objects = [] will fail to store it to localStorage change it to objects = {}.
test it
var objects = [];
objects['objkey'] = {red:'#FF0000'}
var json_str = JSON.stringify(test);
console.log(json_str)
// []
for point 1 and 2, since it using object key name there will be no duplicate, it will be overwritten with new value, no other action needed.
for point 3, if you do objects[objKey] = object; it will append object to last position so oldest position to delete is index 0
function pushToStorage(groupId, objectId, groupIcon, displayString) {
var objKey = "object_" + groupId + "_" + objectId;
var objects = storage.get("objects");
if(objects !== null) {
console.log(objects);
// if not objKey and length objects more than 50 delete oldest
if(storage.isSet(objects[objKey]) === false && Object.keys(objects).length == 50){
// delete oldest object
delete objects[0];
}
}
else {
objects = {};
}
var object = {
groupid: groupId,
objectid: objectId,
groupicon: groupIcon,
display: displayString
};
objects[objKey] = object;
//console.log(objects);
storage.set("objects", objects);
}

Related

How to push new elements to a nested array of objects in JavaScript using spread syntax

I want to create object like this
var items = [{'2021-07-06':[{todo:'Hello'},{todo:'World'}]}]
Here the date should be dynamic so I tried to push value to it like this but this doesn't work
{...items, [CurrentDate] : {...[CurrentDate], todo:'I am here'}}
[CurrentDate] refers to the current date here which is '2021-07-06' and push new todo in array. Also if the date key not present then I want to add new Date eg if '2021-07-08' is not present then add to object and add new todo to it.
You first have to find the right element in items to update (e.g. using findIndex), then access it by index. Then you can update that object using spread syntax.
However, spread is an inefficient approach as it creates a duplicate object for no reason. Likely push is much more efficient (and semantic) as it just adds an element to the array.
The following also handles the case where currentDate doesn't exist in the items array.
let items = [{'2021-07-06': [{todo: 'Hello'}, {todo: 'World'}]}];
function updateItems(items, key, newItem) {
// Find the right object in items
let idx = items.findIndex(obj => obj.hasOwnProperty(key));
// If not found, append a new object
if (idx == -1) {
items = [...items, {[key]: newItem}];
// Otherwise, update existing object
} else {
items[idx] = {
[key]: [...items[idx][key], newItem]
};
}
// Unnecessary but handy
return items;
}
// Update existing object
updateItems(items, '2021-07-06', {todo: 'new item'});
console.log('Update existing item\n' + JSON.stringify(items));
// Add new object
// If using spread, must keep new array returned by updateItems
items = updateItems(items, '2021-07-07', {todo: 'new key and item'});
console.log('Add new item\n' + JSON.stringify(items));
Spread also creates a shallow copy, whereas push doesn't affect any existing elements in the array and is less code to type.
it is easy.
var items = [
{
'2021-07-06':[
{todo:'Hello1'},{todo:'World1'},
{todo:'Hello2'},{todo:'World2'},
{todo:'Hello3'},{todo:'World3'},
{todo:'Hello4'},{todo:'World4'},
],
'2021-07-07':[
{todo:'Hello11'},{todo:'World11'},
{todo:'Hello22'},{todo:'World22'},
{todo:'Hello33'},{todo:'World33'},
{todo:'Hello44'},{todo:'World44'},
]
}
];
let's imagine we want to add
'2021-07-08':[
{todo:'Hello111'},{todo:'World111'},
{todo:'Hello222'},{todo:'World222'},
{todo:'Hello333'},{todo:'World333'},
{todo:'Hello444'},{todo:'World444'},
]
the way we would do so is like this
const newItem = '2021-07-08':[
{todo:'Hello111'},{todo:'World111'},
{todo:'Hello222'},{todo:'World222'},
{todo:'Hello333'},{todo:'World333'},
{todo:'Hello444'},{todo:'World444'},
];
const newTodo = {todo:"Hello5"};
///....
items = [...items, newItem];
/**
* IF YOU WANT TO ADD AN OBJ TO A DATE, DO IT LIKE THIS
*/
items[0]=[...items[0], newTodo];
items is an array, so you need to loop'em items to find the correct index
for (let i=0; i < items.length; i++) {
// before doing the next step validate if the items at position i is equals to an array
const currentItem = [...items[i]];
if (currentItem.anythingYouThinkWouldBeCondition === true) {
currentItem.push(newTodo);
items = [...items,currentItem];
break;
}
}

Check if object exists in an array before pushing

I have an empty array and a selectable tree, and every time when the user is checking or un-checking a node I'm pushing the node's id and if the checkbox is true or false.
But right now if the user will check and then un-check a node there will be two objects in the array of the same node how can i make sure that doesn't happen?
//creating empty array
var checkedItems = [];
//(in kendo observable) on user selection I'm pushing the checked node to array
onItemChecked : function (e) {
var node = e.sender.dataItem(e.node);
checkedItems.push({Id: node.Id, IsChecked: node.checked});
},
You can, before pushing a new object, check the presence of an object that has that id.
var el = checkedItems.filter(function(el) {
return el.Id === node.Id;
});
if (el.length) {
el[0].IsChecked = node.checked;
} else {
// push a new object
}
You can just use an object, which is guaranteed to have unique keys:
var checkedItems = {};
onItemChecked : function (e) {
var node = e.sender.dataItem(e.node);
checkedItems[node.Id] = node.checked;
},
You have a Id which is grate, cuz that can be used for uniqueness. So maybe an array is not the best suited for what you want to accomplish. There are better way to handle it with just plain object (see Mike McCaughan answer) and Map
var items = new Map
items.set(node.Id, node.checked)
// Removing items is as easy as
items.remove(node.Id)
// Getting
var checked = items.get(node.Id)
// Then if you want to iterate over them you would do something like:
for (var [key, value] of items.entries()) {
console.log(key + " = " + value)
}
var values = items.values()
var keys = items.keys()
Map's are like a key/value storage, much like object

How do you search object for a property within property?

I have this object:
key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
And I have an array with only types and I need to add the given image to it, the array looks something like this:
[{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}]
Basically I want to loop the array, find the type in the key object and get the given image and save it into the array.
Is there any simple way to do this?
One thing that stands out here for me is the line
...get the given image and save it into the array
I'm assuming this means the original array. I think a better approach would be to map the appropriate keys and values to a new array but I've assumed, for this example, that it's a requirement.
In an attempt to keep the solution as terse as possible and the request for a lodash solution:
_.each(key, function(prop){
_.each(_.filter(types, { type: prop.type }), function(type) { type.image = prop.img });
});
Given the object of keys and an array of objects like so:
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var arr = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
We can first create an array of the properties in the object key to make iterating it simpler.
Then loop over the array arr, and upon each member, check with a some loop which image belongs to the member by its type (some returning on the first true and ending the loop).
You can change the forEach to a map (and assign the returned new array to arr or a new variable) if you want the loop to be without side-effects, and not to mutate the original array.
var keyTypes = Object.keys(key);
arr.forEach(function (item) {
keyTypes.some(function (keyType) {
if (key[keyType].type === item.type) {
item.image = key[keyType].img;
return true;
}
return false;
});
});
The smarter thing would be to change the object of the imagetypes so that you could use the type as the accessing property, or create another object for that (as pointed out in another answer).
I'm not sure if this solution is modern, but it does not use any loops or recursion.
object = {
spawn: {type:1, img:app.assets.get('assets/spawn.svg')},
wall: {type:2, img:app.assets.get('assets/wall.svg')},
grass: {type:3, img:app.assets.get('assets/grass.svg')},
spike: {type:4, img:app.assets.get('assets/spike.svg')},
ground: {type:5, img:app.assets.get('assets/ground.svg')}
};
arr = [
{type:1, image:null},
{type:3, image:null},
{type:2, image:null},
{type:2, image:null},
{type:5, image:null}
];
var typeImages = {};
Object.getOwnPropertyNames(object).forEach(function(value){
typeImages[object[value].type] = object[value].img;
});
arr = arr.map(function(value){
return {
type: value.type,
image: typeImages[value.type]
};
});
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var typesArray = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
for(var i = 0, j = typesArray.length; i < j; i++)
{
typesArray[i].image = getKeyObjectFromType(typesArray[i].type).img;
}
function getKeyObjectFromType(type)
{
for(var k in key)
{
if(key[k].type == type)
{
return key[k];
}
}
return {};
}
for (var i = 0; i < typesArray.length; i++) {
for (prop in key) {
if (key[prop].type === typesArray[i].type) {
typesArray[i].image = key[prop].img;
}
}
}
It loops through the array ("typesArray"), and for each array item, it go through all the objects in key looking for the one with the same "type". When it finds it, it takes that key object's "img" and saves into the array.
Using lodash (https://lodash.com/):
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var initialList = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
var updatedList = _.transform(initialList, function(result, item) {
item.image = _.find(key, _.matchesProperty('type', item.type)).img;
result.push(item);
});
This will go over every item in the initialList, find the object that matched their type property in key and put it in the image property.
The end result will be in updatedList

Sort javascript key/value pairs inside object

I have some problem with sorting items inside object. So I have something like this:
var someObject = {
'type1': 'abc',
'type2': 'gty',
'type3': 'qwe',
'type4': 'bbvdd',
'type5': 'zxczvdf'
};
I want to sort someObject by value, and this is where I have problem.
I have sorting function that should return key/value pairs sorted by value:
function SortObject(passedObject) {
var values = [];
var sorted_obj = {};
for (var key in passedObject) {
if (passedObject.hasOwnProperty(key)) {
values.push(passedObject[key]);
}
}
// sort keys
values.sort();
// create new object based on Sorted Keys
jQuery.each(values, function (i, value) {
var key = GetKey(passedObject, value);
sorted_obj[key] = value;
});
return sorted_obj;
}
and function to get key:
function GetKey(someObject, value) {
for (var key in someObject) {
if (someObject[key] === value) {
return key;
}
}
}
The problem is in last part when creating new, returning object - it's sorted by key again. Why? And this is specific situation when i have to operate on object NOT on array (yes I know that would be easier...)
Does anyone know how to sort items in object?
Plain objects don't have order at all. Arrays -that are a special types of objects- have.
The most close thing that you can have is an array with the object values sorted . Something like, for example:
_valuesOfAnObjectSorted = Object.keys(object).map(function(k){ return object[k]; }).sort();
You have two possibilities:
Refactor your object into an array
Something like this:
var myObj = [
['type1', 'abc'],
['type2', 'gty'],
...
];
Or even better, since using it somewhere would not rely on array positions but full named keys:
var myObj = [
{name: 'type1', val:'abc'},
{name: 'type2', val:'gty'},
...
];
Use your object with an auxiliar array
Wherever you want to use your object ordered by the keys, you can extract the keys as an array, order it and traverse it to access the object
var ordKeys = Object.keys(myObj).sort(); // pass inside a function if you want specific order
var key;
for (var i = 0, len = ordKeys.length; i < len; i +=1) {
key = ordKeys[i]
alert(key + " - " + myObj[key]);
}
Combination of both of them
If the object is not constructed by you, but comes from somewhere else, you can use the second option approach to construct an array of objects as in the first option. That would let you use your array anywhere with perfect order.
EDIT
You might want to check the library underscore.js. There you have extremely useful methods that could do the trick pretty easily. Probably the method _.pairs with some mapping would do all the work in one statement.

Javascript data structure for fast lookup and ordered looping?

is there a data structure or a pattern in Javascript that can be used for both fast lookup (by key, as with associative arrays) and for ordered looping?
Right, now I am using object literals to store my data but I just disovered that Chrome does not maintain the order when looping over the property names.
Is there a common way to solve this in Javascript?
Thanks for any hints.
Create a data structure yourselves. Store the ordering in an array that is internal to the structure. Store the objects mapped by a key in a regular object. Let's call it OrderedMap which will have a map, an array, and four basic methods.
OrderedMap
map
_array
set(key, value)
get(key)
remove(key)
forEach(fn)
function OrderedMap() {
this.map = {};
this._array = [];
}
When inserting an element, add it to the array at the desired position as well as to the object. Insertion by index or at the end is in O(1).
OrderedMap.prototype.set = function(key, value) {
// key already exists, replace value
if(key in this.map) {
this.map[key] = value;
}
// insert new key and value
else {
this._array.push(key);
this.map[key] = value;
}
};
When deleting an object, remove it from the array and the object. If deleting by a key or a value, complexity is O(n) since you will need to traverse the internal array that maintains ordering. When deleting by index, complexity is O(1) since you have direct access to the value in both the array and the object.
OrderedMap.prototype.remove = function(key) {
var index = this._array.indexOf(key);
if(index == -1) {
throw new Error('key does not exist');
}
this._array.splice(index, 1);
delete this.map[key];
};
Lookups will be in O(1). Retrieve the value by key from the associative array (object).
OrderedMap.prototype.get = function(key) {
return this.map[key];
};
Traversal will be ordered and can use either of the approaches. When ordered traversal is required, create an array with the objects (values only) and return it. Being an array, it would not support keyed access. The other option is to ask the client to provide a callback function that should be applied to each object in the array.
OrderedMap.prototype.forEach = function(f) {
var key, value;
for(var i = 0; i < this._array.length; i++) {
key = this._array[i];
value = this.map[key];
f(key, value);
}
};
See Google's implementation of a LinkedMap from the Closure Library for documentation and source for such a class.
The only instance in which Chrome doesn't maintain the order of keys in an object literal seems to be if the keys are numeric.
var properties = ["damsonplum", "9", "banana", "1", "apple", "cherry", "342"];
var objLiteral = {
damsonplum: new Date(),
"9": "nine",
banana: [1,2,3],
"1": "one",
apple: /.*/,
cherry: {a: 3, b: true},
"342": "three hundred forty-two"
}
function load() {
var literalKeyOrder = [];
for (var key in objLiteral) {
literalKeyOrder.push(key);
}
var incremental = {};
for (var i = 0, prop; prop = properties[i]; i++) {
incremental[prop] = objLiteral[prop];
}
var incrementalKeyOrder = [];
for (var key in incremental) {
incrementalKeyOrder.push(key);
}
alert("Expected order: " + properties.join() +
"\nKey order (literal): " + literalKeyOrder.join() +
"\nKey order (incremental): " + incrementalKeyOrder.join());
}
In Chrome, the above produces: "1,9,342,damsonplum,banana,apple,cherry".
In other browsers, it produces "damsonplum,9,banana,1,apple,cherry,342".
So unless your keys are numeric, I think even in Chrome, you're safe. And if your keys are numeric, maybe just prepend them with a string.
As
has been noted, if your keys are numeric
you can prepend them with a string to preserve order.
var qy = {
_141: '256k AAC',
_22: '720p H.264 192k AAC',
_84: '720p 3D 192k AAC',
_140: '128k AAC'
};
Example

Categories

Resources