Check if object exists in an array before pushing - javascript

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

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

array of unique objects to localStorage

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);
}

checking if more elements of array have same value of key , if they have remove one?

so I've tried everything that i know. Using map and filter, prototypes. Didn't work. .
[{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}]
So what I want to achieve, is when i do ajax, with javascript, to check if two or more object have same value for type, if there is for example two or more bmw-s, remove others and and push just one object with bmw type. Hope i am clear enough.
Thanks in advance
function removeDuplicates(arr) {
var alreadyExist = {}; // hash object to keep track of elemnt that have already been encountered
var indexes = []; // array of indexes that will be removed
arr.forEach(function(o, i) { // for each object o in arr
if(alreadyExist[o.type]) // if the type of the object o at index i already exist
indexes.push(i); // mark its index i to be removed later
else // if not
alreadyExist[o.type] = true; // then mark the object as found so other ones will be removed
});
// for each index in the indexes array
for(var i = 0; i < indexes.length; i++)
arr.splice(indexes[i] - i, 1); // remove the object at that index ( - i because the array arr is continually changing. Its length decrease every time we remove an item)
}
var array = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"red","type":"bmw"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}];
removeDuplicates(array);
console.log(array);
don't remove elements, create a filtered Array:
var yourArray = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}];
var cache = {},
filteredArray = yourArray.filter(({type}) => type in cache? false: (cache[type] = true));
console.log(filteredArray);
It's non destructive, more performant and even simpler and shorter.
Edit: And even without modern features:
var filteredArray = yourArray.filter(function(item){
return item.type in this? false: (this[item.type] = true);
}, {/* the cache-object */}); //abusing `this` to pass the cache object
You can keep track of which types are already present in your array with an object, then only push into new array those that are not present:
var vehicles = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}];
var uniques = [];
var types = {};
for (var i = 0; i < a.length; i++) {
if (!types[a[i].type]) { uniques.push(a[i]); }
types[a[i].type] = true;
}
//uniques = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"black","type":"mercedes"}];

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

Removing an object from a javascript list of objects

I currently have a list of objects in javascript indexed by a key:
var list = [];
list['a'] = [];
list['a'].push({obj: 'test'});
list['a'].push({obj: 'test2'});
list['b'] = [];
list['b'].push({obj: 'test'});
list['b'].push({obj: 'test2'});
I would list to remove the entry based on the key (a/b)
I have tried the following:
for(var x in list) { delete list[x]; }
that works but it actually leaves an undefined entry in the list.
I have also tried splicing the array, but that does not seems to work in this case.
Any thoughts on how to remove the entry in javascript or jQuery?
Thanks.
The Fix:
After reading some of the comments, i was able to better understand what my list is consistent of. Therefor, i was able to do the removal by doing the following:
delete list.b;
I'm not sure if my list is best way to organize my structure, but doing a delete on the list and treating it like an object property did the trick.
Thanks for all the feedback.
I'll assume list is an object, not an array.
If you want to reset a or (or b it's done the same way)
list.a.length = 0;
If you want to delete an element from a at a known index (let index)
list.a.splice(index, 1);
You're attempting to add the elements to the array object as object properties and not as array elements. You can verify this by inspecting the value of list.length (will be 0).
So when doing something such as the following:
function removeProperty(id) {
if (list.hasOwnProperty(id)) {
delete list[id];
}
}
removeProperty('a');
it's really the same as:
delete list.a;
which is why you think it leaves an undefined 'entry' in the 'list'.
You'll need to use a literal object instead:
var list = {};
list['a'] = [];
...
list['b' = [];
...
which would allow you to use delete and have it behave as you expect. Of course you'll lose the .length property on the array but you never had that anyway.
Create a simple prototype for the Array class
Array.prototype.remove = function() {
// Helper function to remove a single element from a list if exists
item = arguments[0]
if (this.includes(item)) {
index = this.indexOf(item)
this.splice(index, 1)
}
}
// Now we can call
myList.remove(YourObject)
The above code will add the remove method to all your lists, so this will help you not just for objects but also strings, integers, or any data type
var list = {1: [{},{}], 2: [{},{}]};
function removeProperty(obj, prop){
if(obj[prop]){
delete obj[prop];
}
}
removeProperty(list,"1");
console.log(list);
If this quote:
I would list to remove the entry based on the key (a/b)
means you would like to select the list to consider based off the key (a/b), then remove elements in the list (or all of them), you can try this:
var list = [];
list['a'] = [];
list['a'].push({obj: 'test4'});
list['a'].push({obj: 'test5'});
list['b'] = [];
list['b'].push({obj: 'test'});
list['b'].push({obj: 'test2'});
var toRemove = 'test4';
var removeFrom = "a";
var consideredList;
for (var prop in list) {
if (prop == removeFrom) {
consideredList = list[prop];
}
}
//Remove everything from the considered list
consideredList.splice(0, consideredList.length);
//Remove based off value, if you know the property name
// for(var pos in consideredList) {
// if(consideredList[pos].obj == toRemove) {
// consideredList.splice(pos, 1);
// }
// }
I made a Plunker of a few different cases (check the script.js file). There seems to be a bit of confusion on what you are after and hopefully this is helpful to you somehow. Good luck.

Categories

Resources