0

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);
};
Odward
  • 103
  • 1
  • 9
  • 4
    if you remove for instance 0, the next addObject should be with id=0? – X-Pippes Oct 23 '13 at 16:58
  • 3
    Why not simply keep track of the last used id and increment ? And why use an array and not a map ? – Denys Séguret Oct 23 '13 at 16:59
  • Is using an array and keeping track of indexes that move when you remove an item instead of an id for each item out of the question? For the best solution, array, hashmap, etc. it would depend on how often items are evicted from the list and what other operations you need. – Daniel Imms Oct 23 '13 at 16:59
  • for that matter why not drop the whole id and just use an array ... it is already indexed ... typeObjects=["null","foo","bar"] – technosaurus Oct 23 '13 at 17:17
  • Feel free to share any feedback about performance of the solution in EDIT 1 :) – Odward Oct 24 '13 at 11:12

5 Answers5

1

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.

Joe
  • 42,600
  • 24
  • 134
  • 225
  • 3
    How will this solve the problem of finding the lowest-numbered "id" value to store a new entry? – Pointy Oct 23 '13 at 17:00
  • 1
    And what happens when you remove structure.objects[1], and go to add? The OP wants the next item to go to structure.objects[1] instead of the next-highest index. – Jaime Torres Oct 23 '13 at 17:05
  • This doesn't answer it as items will be removed (`removeObject` function in Q) – Daniel Imms Oct 23 '13 at 17:05
  • 1
    I know what the question asked, but it's worth rethinking the actual requirements, which can become mixed up with implementation details. This almost certainly fulfils the original requirement. Numbers are cheap. As exebook says, will the counter ever reach maxint? – Joe Oct 23 '13 at 17:07
  • @Joe - works fine, but nearly exactly duplicates an Array, with missing Array-like builtin functions. topId~=length add~=push,... – technosaurus Oct 23 '13 at 18:25
  • @technosaurus Of course, you use the best available data structure for your problem. An Array will constantly grow in size as you add and remove elements and never shrink. In a use case where adding and removing are frequent this might become a problem. – Joe Oct 23 '13 at 18:37
  • @Joe `structure.objects[id] = item;` How is that better? Instead of having an array to operate on directly you have an array within an object within another with a constructor that duplicates a subset of Array, specifically .push() and .length This will be slower (objId needs to be deferenced), larger (objId duplicates .length and has to be stored) and more complex (code will now have to reference objId rather than a simple index) than using a standard array (which has a plethora of native methods designed and optimized to handle growth and removal). – technosaurus Oct 23 '13 at 19:10
  • Speed of access and space as a ratio of number of elements is negligible in each case, I can't see that making a massive difference either way between implementations. But an ever-growing array is a problem. – Joe Oct 23 '13 at 20:46
  • It may be redundant/suboptimal, but what the hell +1 just for implementing dot notation for numbered arrays in a relatively clean way. – technosaurus Oct 23 '13 at 23:01
  • Thanks for the upvote. But I really don't agree that we can say what is and isn't optimal or suboptimal without knowing more about the problem. Also, JavaScript objects are the most used data structure of the language. There's absolutely nothing wrong with using them. They exist to be used. – Joe Oct 24 '13 at 10:18
1

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
X-Pippes
  • 1,112
  • 7
  • 23
0

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;
    }
}
Jaime Torres
  • 9,715
  • 1
  • 45
  • 55
  • What is `open`? Something like this seems like the obvious, trivial solution. It would lead to O(n) worst-case time when there are no gaps though. – Daniel Imms Oct 23 '13 at 17:07
  • A typo --- it should have been idx. I agree that it is obvious/trivial, but that's pretty much what the OP asked for. If you're going to fill a first available gap by way of lowest numeric, you need O(n^2). This could be made more efficient via a different data structure, but not if you want to maintain the requirement proposed by OP. – Jaime Torres Oct 23 '13 at 17:08
  • Actually scratch that, this would be O(n^2) worst-case. – Daniel Imms Oct 23 '13 at 17:08
0

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

exebook
  • 27,243
  • 27
  • 105
  • 196
0

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.

Community
  • 1
  • 1
technosaurus
  • 7,101
  • 1
  • 28
  • 49
  • Finding the first empty field will be O(N), that could get very large (true for any implementation). Alternatively, if you want to just use the topId, you will end up with 'holes' in a sparse array that grows monotonically. – Joe Oct 23 '13 at 18:42
  • @Joe - but finding field N will take O(1) which is typically more common than insert/delete that is why I recommended removing (not just deleting) then adding new elements to the end unless there is a _reason_ for keeping it sparse, for instance the id corresponds to a customer account that may later be reinstated or something. I made a test case for comparison here: http://jsperf.com/array-vs-object-performance/12 – technosaurus Oct 23 '13 at 19:46
  • Great that you did a performance comparison but lookups are only one operation and there's only about 20% difference anyway. No point having a protracted argument over it, performance will probably be trivial in any case, – Joe Oct 23 '13 at 20:35
  • Its almost 100% faster in chrome and 250% faster in IE10 so far. You are looking up an object by a key in your example vs an array by index. Try an add or modify comparison and you will get similar results. Its not a problem with your implementation per se, just a characteristic comparison of native objects to user objects. – technosaurus Oct 23 '13 at 21:01
  • Sure. Of course an array index is always going to be faster than a hashtable. You could look at that and observe that the hashtable implementation is very impressive if it's the same order of magnitude as an array lookup. Either way, I just don't think it's a massive issue compared to the concerns I've already given, unless the original poster says "and I want to insert 1000 a second". Premature optimisation and that. – Joe Oct 23 '13 at 21:10