Get the lower integer id not already used in Javascript - javascript

I have a list of JS objects defined by an integer ID.
objects = [{
id: 0,
type: 'null'
}, {
id: 1,
type: 'foo'
}, {
id: 2,
type: 'bar'
}];
I implemented a function to remove an element from my list :
removeObject = function(o){
objects.splice(objects.indexOf(o), 1);
}
My problem is that I need to create a function to add a new item in my list with a id not already used (for example the lower positive integer not present in the list).
I tried to do something like that but it did not work when I remove the object 0 (for example).
addObject = function(type){
objects.push({
id: objects.length,
type: type
});
};
How can I do this ?
EDIT 1
According to your answers, I assume that the best solution in term of performance is to just use a topId which is always incremented when I add a new object in my list.
But that do not answer to my requierement. Actually I think that #X-Pippes response could be good.
Should I do someting like that :
objects = [{
id: 0,
type: 'null'
}, {
id: 1,
type: 'foo'
}, {
id: 2,
type: 'bar'
}];
// Init available ids list with the default value
availableIds = [objects.length];
removeObject = function(o){
// Remove the object from the list
objects.splice(objects.indexOf(o), 1);
// Add its id to the available ids list
availableIds.push(o.id);
}
addObject = function(type){
// Get lower id available
var newId = Math.min.apply(Math,availableIds);
// Push the new object with the id retrieved
objects.push({
id: newId,
type: type
});
// Remove used id from the available ids list
availableIds.splice(availableIds.indexOf(newId), 1);
// Add a default id if available list is empty
if(availableIds.length < 1) availableIds.push(objects.length);
};

if you remove for instance 0 and the next addObject is 0 you have to do something like:
keep a list [initial empty] with every ID removed. When you need to add a new one, pick the shorter, add and delete from list.
Also keep a var with the biggest ID added. If the previous list is empty, add +1 to the var and addObject with that id

Use the correct structures. A JavaScript object will do the job. It guarantees that you only get one item for key, you can look up and remove by key in probably O(1)ish. No point trying to re-implement it in a less efficient manner, which will be O(n) lookup.
var structure = {
objects : {},
topId : 0
}
structure.add = function(item) {
var id = this.topId ++;
structure.objects[id] = item;
}
structure.add("thing")
structure.add("other thing")
structure.add("another thing")
structure.objects
>>> Object {0: "thing", 1: "other thing", 2: "another thing"}
structure.objects[1]
>> "other thing"
Then the normal index operations to get/set/delete.
If you use that function then you have an invariant (guarantee) on your data structure that you won't use the same ID twice.

You need a function to find the first free number:
addObject = function(type){
objects.push({
id: firstOpenIndex(),
type: type
});
};
firstOpenIndex = function() {
for(var idx = 0; true; i++) {
var found = false;
for(var o in objects) {
if (objects[o].id == idx) {
found = true;
break;
}
}
if (!found) return idx;
}
}

In Javascript MaxInt is 9007199254740992. Why not just keep incrementing?

You can and probably should just use an array(s) like:
objects.type=['null','foo','bar'];
to add an object see:
How to append something to an array?
to find a value: var index = objects.type.indexOf('foo');
to find 1st empty field var index = objects.type.indexOf(''); which you can use to find the element for adding (if index is -1 use objects.type.length) if you "delete" an element by setting it to "" or... unless you have specific reason for keeping the "ID" static (in this case the array index), remove the element and only append new ones to the end
to remove an element see:
How do I remove a particular element from an array in JavaScript?
which will allow you to just push/append the next data.
if you need a new object array with empty fields to fill because you get new data to track:
object.newField=new Array(objects.type.length);
If you get to this point where your object contains multiple arrays, you will probably want to create functions for insert/add and delete/remove, so you don't do an operation on 1 and not the other.
Everything is already built in (read likely already pretty fast) and you don't need to reinvent constructors for your really cool object type.

Related

What is the difference between assignment of object with property, or assigning to property?

I was learning to edit an array with a function and came across these two similar functions. I was wondering what is the difference between them. They both worked the same for me, but are there more pros or cons to using one of them?
function edit(position, newTodoText){
userTodos[position] = ({todoText: newTodoText});
console.log(userTodos);
}
and
function edit(position, newTodoText){
userTodos[position].todoText = newTodoText;
console.log(userTodos);
}
One difference is that the second option requires userTodos[position] object to already exists and would cause an exception if it doesn't.
Uncaught TypeError: Cannot set property 'todoText' of undefined
Another difference is that the second option performs mutation (which means it edits an existing todo) while the first option creates a new todo at the same position (and the old one is deleted via something called garbage collection)
Here's some code with commentary to illustrate the differences:
function optionOneInserts(x) {
x[0] = { name: "alice" };
}
function optionTwoMutates(x) {
x[0].name = "alice";
}
let x1 = [{ name: "bob" }];
let x2 = [{ name: "bob" }];
let previousFirstItemInX1 = x1[0];
let previousFirstItemInX2 = x2[0];
console.log("Current first item in x1 and x2:");
console.log(`current x1[0] = ${JSON.stringify(previousFirstItemInX1)}`);
console.log(`current x2[0] = ${JSON.stringify(previousFirstItemInX2)}`);
console.log(`\n...performing options one and two`);
optionOneInserts(x1);
optionTwoMutates(x2);
console.log("\nboth options end up with same values in x1 and x2:");
console.log(`x1 = ${JSON.stringify(x1)}`); // [{ name: 'alice' }]
console.log(`x2 = ${JSON.stringify(x2)}`); // [{ name: 'alice' }]
console.log(
"\nbut the first option leaves its old first value untact and adds a new value, while the second option changes the old value instead:"
);
console.log(`previous current x1[0] = ${JSON.stringify(previousFirstItemInX1)}`);
console.log(`previous current x2[0] = ${JSON.stringify(previousFirstItemInX2)}`);
The first form will create a new object at the given position with only one property while the second form will extend an already existing object at that position with the given property and contents.
For the second form to be a bit more "tolerant" you could modify it to
function edit(position, newTodoText){
(userTodos[position]=userTodos[position]||{}).todoText = newTodoText;
console.log(userTodos);
}
const userTodos=[{done:"object created"},{todoText:"nothing"}];
edit(3,"something else");
edit(2,"and also this");
edit(0,"fill it up!");
(userTodos[position]=userTodos[position]||{}) will create a new object at the given position if it does not yet exist, otherwise it will continue to work with (=extend) the already existing object.

Optimizing my KnockOutJS function - From Ugly Code to Clean, How?

Function Description
This function is supposed to simply replace an item in my observable array.
I am accepting 3 parameters:
findBy = Whether to locate the source index by "id" or "name" property.
cmpVal = The value we are searching the array for
objNewItem = The new item. This case ex, { id: "12" , name: "NewName" }
Then I am deleting that index from the array, and finally pushing the new object into the array.
I had no luck using the replace function from Knockout, so I had to write my own.
I realize this may be the ugliest code on the internet. That's why I am deferring to you professionals here. :)
/* Update A Station
*
* #param findBy string Property to find ( "id" or "name")
* #param cmpVal string Value to compare against
* #param objNewItem object The new object replacing old
* */
self.modifyStation = function(findBy, cmpVal, objNewItem){
var sourceIndex;
var oldId;
/* Find Index Of Old Station */
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
switch (findBy) {
case "id":
var value = item.id();
if(value == cmpVal){
sourceIndex = c;
oldId = item.id();
}
break;
case "name":
var value = item.name();
if(value == cmpVal){
sourceIndex = c;
oldId = item.id();
}
break;
}
});
/* Remove Old */
self.station().splice(sourceIndex,1);
/* Insert New Station
* [For Now] not allowing updating of ID. Only
* can update the other properties (yes, I realize that
* only leaves "name", but more will be added )
*/
objNewItem.id = oldId; // Put old ID back in
self.station.push(objNewItem);
}
Note: I am not allowing them to edit the ID for now.
Can anyone help me clean this up? I am smart enough to know its not efficient, but I don't know how else to optimize it.
Any advice would be appreciated. Thank you!
John
Ok so...
First of we will create a function that will contain all the logic, from this example on you can do anything with it. The most proper way would be to extend your 'observableArray' directly on knockout, but i am not going to get this one this far now :P
function ReplaceInObservableArray(obsArray, prop, cmpVal, newItem){
// use the fact that you can get the value of the property by treating the object as the dictionary that it is
// so you do not need to program for specific properties for different array model types
var foundItems = obsArray().filter(function(i){
return ko.utils.unwrapObservable(i[prop]) == cmpVal;
});
if(foundItems.length > 1)
{
// handle what happens when the property you are comparing is not unique on your list. More than one items exists with this comparison run
// you should throw or improve this sample further with this case implementation.
} else if(foundItems.length == 0)
{
// handle what happens when there is nothing found with what you are searching with.
} else {
// replace at the same index rather than pushing, so you dont move any other items on the array
// use the frameworks built in method to make the replacement
obsArray.replace(foundItems[0], newItem);
}
}
var demoArray = ko.observableArray([{ id : 1, name : 'test1' },{id : 2, name : 'test2' },{ id : 3, name : 'test3'},{id : 4, name : 'test4'}]);
ReplaceInObservableArray(demoArray,'id', 1, {id : 1, name : 'test111'});
ReplaceInObservableArray(demoArray,'name', 'test3', {id : 3, name : 'test3333'});
console.log(demoArray());

JavaScript function Array.prototype.includes() doesn't work in for loop

I have a web app that tracks your purchases and displays different statistics. In one of the pages I have jQuery ajax requests that load a user's purchases from an API call. Then all the purchases are put into a global array, called G_PURCHASES, as JavaScript objects.
So far so good. Then I call a function that uses jQuery's Deferred() to make it chainable; it iterates G_PURCHASES and gets all distinct purchase.item.category's (take a look at the purchase object's relevant structure) by checking if G_PURCHASES[i].item.category is included in another global array called G_CATEGORIES by using Array.includes(). And if it is not then push() it into G_CATEGORIES.
The error I have a problem with is that even after a category object has been pushed into the G_CATEGORIES array, the Array.includes() check still returns false, every time. Check the relevant code and output to clear it up.
// Relevant structure of the purchase object
purchase = {
item: {
category: {
categoryID: int,
name: string
}
}
// Relevant code
var G_PURCHASES = []; // array declared globally
// it is successfully filled with #purchase objects from another function
var G_CATEGORIES = []; // array declared globally
// to be filled by #LoadAllCategories
var LoadAllCategories = function() {
// make a jQuery Deffered
let func = $.Deferred(function() {
let allP = G_PURCHASES; // make a shortcut
let allC = C_CATEGORIES; // make another shortcut
for (var i = 0; i < allP.length; i++) {
// get whether the current purchase.item.category
// already exists in allC array
let exist = allC.includes(allP[i].item.category);
// console.log the above result
console.log('i = ' + i + ', category exists = ' + exist);
// if it doesn't exist then push it in
if (!exist) allC.push(allP[i].item.category);
}
this.resolve();
});
return func;
}
// Input
G_PURCHASES has 6 #purchase objects with 3 unique item.category 'ies
// Output
i = 0, category exists = false
i = 1, category exists = false
i = 2, category exists = false
i = 3, category exists = false
i = 4, category exists = false
i = 5, category exists = false
// Result
G_CATEGORIES contains duplicate categories
I tried to use Array.indexOf() and jQuery's $.inArray() with no success. No matter what I console.log() I can't seem to find where the error lies. So, if you can tell me why does Array.includes() doesn't work inside this for loop I will find you and I will buy you a beer!
Well includes checks for referential equality, so there might be two objects with same properties and values, but still they are different objects, thus their reference is not equal. You probably want to check every category object for their categoryID and name manually to find duplicates.

accessing field value in array of objects in javascript

I need to access an element with a certain field value for the cdOption field in this array of objects of type possibleOptions:
[Object { cdOption="OPT001", description="Description 1", type="STRING"},
Object { cdOption="OPT002", description="Description 2", type="STRING"},
Object { cdOption="OPT003", description="Description 3", type="STRING"}]
The field value I'm looking for is extracted from antoher object in an array and so I'm alreay in a $.each cycle.
Can I avoid entering another cycle in order to loop the possibleOptions object and look for the specified field value?
I've tried with
possibleOptions[option.cdOpzione] but it doesn't work, is there a way to do this? I know I'm missing something.
current $.each code:
$.each(oldOptions, function(key, option) {
$.each(possibleOptions, function(key, possibleOption) {
if (option.cdOption === possibleOptions.cdOption) {
console.log(option.cdOption);
console.log(possibleOption.description);
}
});
});
In a generic way, you can't avoid the extra cycle. There may be particular solutions though, depending on your circumstances.
Solution 1
You could avoid it if you restructure your data, to have possibleOptions be an object with the values in cdOption as keys and an object with description and type as value.
Example:
var possibleOptions = {
'OPT001' : { description:"Description 1", type:"STRING" },
'OPT002' : { description:"Description 2", type:"STRING" },
'OPT003' : { description:"Description 3", type:"STRING" }
};
var val = 'OPT002';
console.log(possibleOptions[val]);
Solution 2
Another thing you could do if the cdOption is always of the form OPT-index- where -index- is 1+ the index in the array is to parse the value you're looking for, extract the -index-, parseInt and subtract one.
Example:
var val = 'OPT002';
var index = parseInt(val.substring(3))-1;
console.log(possibleOptions[index]);
Demo for both: http://jsbin.com/opojozE/1/edit
Array.filter can return an array of the elements matching a conditon. e.g. if you want to find the object (or objects) with cdOption == "OPT002", you could say:
var matches = possibleOptions.filter(
function( element ) {
return ( "OPT002" == element.cdOption );
}
);
and matches will contain:
[
{ cdOption="OPT002", description="Description 2", type="STRING"}
]
if you're just looking for one match:
var myOption = (matches.length > 0) ? matches[0] : null;
If you need to support older browsers that lack Array.filter, see Array filter method at MDN for a way to add it.

Delete an element inside a JSON array by value with jQuery

Part of my json Array
var videos = $j.parseJSON('
[
{ "privacy":"public",
"id":"1169341693" },
{ "privacy":"private",
"id":"803641223" },
{ "privacy":"public",
"id":"1300612600" }, ......
When I console.log the element I'm getting
[Object, Object, Object, …]
0: Object
privacy: "public"
id: "1169341693"
1: Object
privacy: "private"
id: "803641223"
2: Object
privacy: "public"
id: "1300612600"
I also have a unique id I want to search for
var uniqueId = 803641223;
I want to find, in my videos array, the right id, and delete that whole array element. So In that case, I want my final videos array to contain only 2 object, instead of 3 :
var videos = $j.parseJSON('
[
{ "privacy":"public",
"id":"1169341693" },
{ "privacy":"public",
"id":"1300612600" }, ......
My problem is how to get in the array to do my splice. I prefer to do it with jQuery
Any help please?
You can use grep :
videos = $.grep(videos, function(e) { return e.id!='803641223' });
In vanilla JavaScript you could have used the similar filter function but it's not supported by IE8.
Please note that videos is a JavaScript array, it's not a JSON array, even if it was made by parsing a JSON string.
A non-jQuery solution that modifies the array in place:
var uniqueId = 803641223;
var videos = [
{ "privacy":"public",
"id":"1169341693" },
{ "privacy":"private",
"id":"803641223" },
{ "privacy":"public",
"id":"1300612600" }
];
function cleaner(arr, id) {
for (var i = 0; i < videos.length; i++) {
var cur = videos[i];
if (cur.id == uniqueId) {
arr.splice(i, 1);
break;
}
}
}
cleaner(videos, uniqueId);
http://jsfiddle.net/4JAww/1/
Note that this modifies the original array in place, such that the original videos array will have the items you want, and the one that matched the uniqueId will be gone (forever). So it depends on whether you want to be able to access the original array ever again, or are okay with modifying it.
It just loops through the elements of the array, compares the item's id property to the uniqueId value, and splices if they match. I use break; immediately after the splice because you seem to imply that the uniqueId can/should only appear once in the array since it's...unique.
Hello you can remove element with javascript splice function...
videos.items.splice(1, 3); // Removes three items starting with the 2nd,
It worker for me.
arrList = $.grep(arrList, function (e) {
if(e.add_task == addTask && e.worker_id == worker_id) {
return false;
} else {
return true;
}
});
It returns an array without that object.
Hope it helps.

Categories