How to iterate over an array of objects in JavaScript? - javascript

I'm using PHP to fetch "tasks" from my database and encoding it as JSON. When I transfer the data over to javascript, I end up with something like this:
Array {
[0] => Task {
id: 2,
name: 'Random Task',
completed: 0
}
[1] => Task {
id: 8,
name: 'Another task',
completed: 1
}
}
etc.
I guess my real question is, what's the most efficient way to find the task by its id? Iterating through the array and checking each object seems like it might not be the most efficient? Is there any other way to do this?

The thing about Javascript objects is that they are essential maps. You can access properties through using both dot notation ("object.property") and also index notation ("object["property"]). You can also enumerate through its properties, either using a for (i...) or for (in...)
for (var i = 0; i < arrayObj.length; i++) { ... }
for (var prop in arrayObj) { ... }
What I have been doing recently is building some Linq-esque extensions to the array object:
Array.prototype.Where = function(predicate) {
Throw.IfArgumentNull(predicate, "predicate");
Throw.IfNotAFunction(predicate, "predicate");
var results = new Array();
for (var i = 0; i < this.length; i++) {
var item = this[i];
if (predicate(item))
results.push(item);
}
return results;
};
Ignoring my custom Throw type, it basically allows you do to something like:
var item = arrayObj.Where(function(i) { return (i.id == 8); }).FirstOrDefault();
I'll publish it all at some point if you are interested?

Usually the most efficient way to iterate over an array collection in Javascript is to stick to the native for loop. The reason I say "usually" is that the implementation comes down to each unique browser's implementation of javascript so there is no absolute definitive answer.
There's a nice post at http://solutoire.com/2007/02/02/efficient-looping-in-javascript/ which covers the performance of each of the main iteration methods and empirically comes to the same conclusion.

If you don't need to maintain order, then the best way is to a regular object, and index by task id. That gives you O(1) access.
var tasks = {
'2': {
id: 2,
name: 'Random Task',
completed: 0
},
...
}
If you also need ordering maintained, then write an OrderedMap "class" that maintains the order by creating an array of task ids, but the actual tasks will still be stored in an object indexed by task id. So essentially you would have:
// internal API (to help maintain order)
taskIDs = [a, b, c, ..];
// internal API (for actual storage)
tasks = {
a: { .. },
b: { .. },
};
// external API for iterating objects in order
forEach(fn);
// external API for accessing task by ID
get(id);
The outside world can be ignorant of how you maintain order as long as you provide a nice encapsulated way of iterating these in order, and accessing them by task id.
If you need reference for implementing such a class, see the source for LinkedMap from Google Closure Library.

Just a little more food for thought, this is what I ended up with:
this.find = function (test) {
var results = [];
for (var i = 0,l = this.tasks.length; i < l; i++) {
var t = this.tasks[i];
if (eval(test)) {
results.push(this.tasks[i]);
}
}
return results;
}
this allows me to do a simple tasks.find('t.id == 2') or tasks.find('t.completed == 1');

If id is unique (and mostly continuous) you can do a one time rearrange of the array so that array index reflects the id. If they're not unique, you can sort them and do a binary search.
But this would be useful only if you access the items by id from the array frequently, otherwise the overhead of sorting won't be worth it.

Is your array large? If not, you probably won't win many microseconds on optimizing it.
If it is large, you should really return a dictionary instead (As Matthew Flaschen commented), which uses the task's ID as key. In this way you'll get constant time lookup (atleast if the javascript implementation is optimal).
Just use a ordinary PHP associative array, and run it through json_encode or whatever you're using.
//Assume you have your original Array named $tasks:
$dictionary = Array();
foreach($tasks as $task)
$dictionary[$task->getID()] = $task;

Related

How to get first n elements of an object using lodash?

I want to get the first n key/value pairs from an object (not an array) using lodash. I found this answer for underscore, which says to use use first (doesn't exist in lodash), or to use take (only works on arrays).
Sample node session trying to get the 'a:7' and 'b:8' pairs from an object:
> var ld=require("lodash")
undefined
> var o={a:7, b:8, c:9}
undefined
> ld.keys(o)
[ 'a', 'b', 'c' ]
> ld.take(o, 2)
[]
> ld.first(o, 2)
undefined
>
Surely, there must be some easy way to do this with lodash, but for the life of me I can't find anything. Maybe I have to resort to native js?
You cannot take the first N elements of an object without writing custom code. This is because there is no ordering of the elements in objects, so if there were a library function for it, it would never be guaranteed to give you the elements you expect. Given an object like
var obj = { b: 3, y: 2, a: 1 };
it is not clear what the "first 2" refers to - do you want a and b because that is the alphabetic order? If so are they in that order or not? If not, do you want b and y because they appear first? Perhaps you want a and y because of their values being the lowest?
There is no guarantee for what you will get aside from not getting duplicates, so all of those combinations are valid. Furthermore, you can get them in any order y and a is equally valid output You may prefer one or another but it doesn't make it correct in general.
There are ways around this and but you have to accept that you need to deal with the non-order.
Pure JavaScript solution.
function firstN(obj, n) {
return Object.keys(obj) //get the keys out
.sort() //this will ensure consistent ordering of what you will get back. If you want something in non-aphabetical order, you will need to supply a custom sorting function
.slice(0, n) //get the first N
.reduce(function(memo, current) { //generate a new object out of them
memo[current] = obj[current]
return memo;
}, {})
}
var obj = { b: 2, y: 25, a: 1 }
console.log( firstN(obj, 2) );
This is using Object.keys, Array.prototype.sort, and Array.prototype.reduce
The same can be achieved with lodash but not vastly more concise than this - it would involve calling similar functionality. It can be done like this, for example:
function firstN(obj, n) {
return _.chain(obj)
.keys()
.sort()
.take(n)
.reduce(function(memo, current) {
memo[current] = obj[current];
return memo;
}, {})
.value();
}
var obj = { b: 2, y: 25, a: 1 }
console.log( firstN(obj, 2) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
As you can see, it's pretty much the same as before. There can be variations on the syntax and the exact means of how you do this task, but the major points should still be there - you need to sort for consistency and then you can get any number of items.
If you look at the loadash documentation for first. It only takes in an array as its argument, and this is probably not the API to use.
See: https://lodash.com/docs/3.10.1#first
Here is 1 method you can solve it using standard Javascript API.
The catch here is that you can use the Object.keys(...)[index] API to retrieve the element's key based on their position.
Then all you need to do is just loop n number of times and using the derived key push it into another object.
var firstN = 2;
var o={a:7, b:8, c:9};
var result = {};
for (var index=0; index < firstN; index++) {
var key = Object.keys(o)[index];
result[key] = o[key];
}
console.log(result);
There is no straight-forward solution. Inspired by previous answers and comments, and reading a couple of articles about non-guaranteed properties order, I created a solution where I sort the keys first.
Sorting the keys
Here is a usable one-line approach with sorted keys (and therefore guaranteed order).
Chaining
_.chain(object).toPairs().sortBy(0).take(2).fromPairs().value()
Without chaining
_.fromPairs(_.take(_.sortBy(_.toPairs(object), 0), 2)),
Details on sorting
The sortBy(0) sorts our collection by keys (index 0). The original object is at first converted by toPairs() to an array of pairs (each pair is an array [key, value]) and then sorted by the first values of these pairs (the key has index 0 in the pair).
Important: As mentioned in previous answers and comments, the order of properties cannot be guaranteed, even in the latest ES versions. See this updated SO answer. Therefore I am sorting the keys.

Understanding lists in JavaScript

I am reading through Eloquent JavaScript and have been stuck trying to understand lists for about two days so I figured I would finally ask a question. The example they give in the book is:
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
Now I think I understand the example... There is a list object and it has properties value and rest. Then, rest has properties of value and rest, etc... However, I don't understand what rest is or even stands for. Does the rest property contain an object? So, list.rest.value would == 2? How is this useful? Some ways I could see this as useful are having a list Car, with prop engine, gauge, etc, with further properties of accelerate, brake, low fuel... How would something like this be achieved?
I do apologize for the "all overness" of this post, I don't exactly know what to ask or how to phrase it. It seems like the book only explained objects and properties, but never actually having objects as an objects property.
Thank you all in advance, and if you need any clarification or more info I will try to provide it.
This code simply uses JavaScript Object Notion to define an object named list.
// Would simply define an empty object.
var list = {};
Now you can add some properties to the object.
// Would define an object with a single property: `value`.
var list = {
value: 1
};
Using nested object declarations, you can give the list object child objects as well:
var list = {
value: 1,
rest: {}
};
Now list.rest is an empty object. You can fill that out by adding some properties:
var list = {
value: 1,
rest: {
value: 2
}
};
And your nesting can continue ad-infinitum. The object in your original post, the following is possible:
console.log(list.value); // 1
console.log(list.rest.value); // 2
console.log(list.rest.rest.value); // 3
It's important to understand that this in no way creates a class or includes any additional methods with the object. It seems to be structured as a linked list but provides no functionality to add/remove/modify (except by directly modifying the original object).
In the example above the list variable is an associative array. This is JavaScript's version of an "object". While the property list.value ends up being typed as an integer, the property list.rest is typed as a nested associative array. The properties themselves can be any valid type. Many jQuery plugins are coded where the properties themselves are actually delegate functions.
The object you have described above in the example does not seem to me to be terribly useful beyond being an example of how this kind of object can contain references to other objects. However, when you begin applying this in an "object oriented" concept (keep in mind that it is not truly object oriented), it becomes more useful. You can then create your own "namespace" with properties, functions and delegates that can be re-used time and again.
Thank you all for your information. I don't know if there is a best answer selection on this site or not, but I really do appreciate the help Justin, Joel, and Evan. I think the main part I was confused about is just practical application for real applications. I have messed around a little bit and came up with this and have a much better basic understanding now:
var car = {
engine: {
turn_on: "Turned engine on",
turn_off: "Turned engine off",
desc: {
size: "V6",
year: 2000
}
},
fuel: {
level: 55
}
};
function CheckFuel(fuel){
if(fuel > 50){
console.log("In good shape");
}
else{
console.log("We should fuel up");
}
}
console.log(car.engine.turn_on);
console.log(car.engine.turn_off);
console.log(car.engine.desc.size);
console.log(car.engine.desc.year);
CheckFuel(car.fuel.level);
Now time to practice iterating through. Thanks again!
This is an implementation of a linked list. Each node in the list has a reference to the next node. 'Rest' is an object (the next node in the list) that also contains every other node in the list (via it's rest property).
The first value in the list would be list.value;. The second value in the list would be list.rest.value;. The items in the list can be shown as:
item1 = list;
item2 = list.rest;
item3 = item2.rest;
This continues until itemX.rest is null.
These two functions could be used to manage the list and may help you understand how iterating through it would work:
function addToList(item)
{
if(!list)
{
list = item;
return;
}
var temp = list;
while(temp.rest)
{
temp = temp.rest;
}
temp.rest = item;
}
function printList()
{
var temp = list;
while (temp)
{
print temp.value; //i'm not sure what the javascript print function is
temp = temp.rest
}
}
The add function would be called like this: addToList({ value:10, rest:null });

Javascript: Change name of an array into a string

Below, I have an array of arrays of objects. I go through looking for my object, and once I find which array it's in, I want to get at and work with that array's name as a string. My guess, was something like Array.name (as it plays out below), but that doesn't work.
ActiveDocument.gaShapesTab1 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape2"]);
ActiveDocument.gaShapesTab2 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape4"]);
ActiveDocument.gaShapesTab3 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape6"]);
ActiveDocument.gaShapeArrays = new Array(gaShapesTab1, gaShapesTab2, gaShapesTab3);
// go through an array of arrays
for(var x=0; x<gaShapeArrays.length; x++)
{
// and go through the objects of each one
for(var y=0; y<gaShapeArrays[x].length; y++)
{
// if "object" is in the array
if(object == gaShapeArrays[x][y])
{
// get "sidetab" from object's array's name
var sidetab = gaShapeArrays[x].name.replace('gaShapes',''); // assumes that shapearrays will have naming convention gaShapesSidetab
// we found it, we can stop now
break;
}
}
}
I'm working in Hyperion Intelligence, so not all Javascript will apply. For instance I don't have access to window or document.
Each array contains a set of shape objects related to a visual tab. This allows me to show or hide or do more complex operation with what's on each tab simply by calling the array of shapes. But, when working with the shapes, themselves, I need to know which tab they're on. I'm trying to work backwards by finding which array they're in.
You don't want to do that.
If you really need to find a value in several arrays and then pull out an identifier, then you want a dictionary, not named variables:
var dictOfArrays = {
'evens': [0,2,4,6,8,10],
'odds': [1,3,5,7,9]
};
This stores the identifier that you seek as data, so you can store that identifier and use it later to retrieve the value if you want:
var whichArrayKey = findMyValuesKey(value, dictOfArrays);
console.log('Value '+value+' is in array keyed '+whichArrayKey);
var matchingArray = dictOfArrays[whichArrayKey];
var firstValueInMatchingArray = matchingArray[0];
The name of a variable is just something for you, the developer, to use to know which thing is which. It's just a handle for a place in memory where stuff is stored. As such, it doesn't mean anything to the code. If you actually want to use it in the program, then it is data, not code, and should be encoded in a data structure like a dictionary as above. That way you can pass the array or the identifier around as much as you please, and the behaviour of the code doesn't have to be tied to the names you give your variables.
Edit 1:
The newly added code, in dictionary form/object notation:
ActiveDocument.gaShapeArrays = {
'gaShapesTab1' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape2"]
],
'gaShapesTab2' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape4"]
],
'gaShapesTab3' : [
ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],
ActiveDocument.Secti‌​ons["Dashboard"].Shapes["Shape6"]
]
}
So each key (e.g. 'gaShapesTab1') is paired with an array value ([...]). This is instead of using new Array() everywhere.
Once you have found the key of the array containing a reference matching your object, you'll have that key as a string (e.g. "gaShapesTab3"). You can't change this string in-place, and I don't think you'd want to. If you could clarify why you need to change the name of the array, perhaps it will be clear how to resolve the problem. For example, do you have other code that needs the array to have a particular name?
Array's name? Arrays do not have names. You only have variable names, variables that store your arrays. If you have a two-dimensional array, you need to grab the "coordinates".
So:
if(object == gaShapeArrays[x][y])
{
// Found the object! It's in [x][y], so in array gaShapeArrays[x] which
// itself is in gaShapeArrays
}
Even though I think #Phil H gave me the answer to my question, as the proper way to do it, I have other reasons to do it the way #ben336 was commenting. It might not be proper, but I'm posting what the solution was in the end. Fortunately, I already had the gaSidetabs array elsewhere in my startup script for another function. I just assigned a string value to the .name property of each array. Would've been nice to know if there was a way to "get at" the symbolic name (or whatever you want to call it) that I called the array, but it sounds like that's just not possible.
ActiveDocument.gaShapesTab1 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape1"],ActiveDocument.Sections["Dashboard"].Shapes["Shape2"]);
ActiveDocument.gaShapesTab2 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape3"],ActiveDocument.Sections["Dashboard"].Shapes["Shape4"]);
ActiveDocument.gaShapesTab3 = new Array(ActiveDocument.Sections["Dashboard"].Shapes["Shape5"],ActiveDocument.Sections["Dashboard"].Shapes["Shape6"]);
ActiveDocument.gaShapeArrays = new Array(gaShapesTab1, gaShapesTab2, gaShapesTab3);
ActiveDocument.gaSidetabs = new Array('Tab1','Tab2','Tab3');
// Assigns a .name javascript property to each array. assumes the order and length of the arrays is the same.
if (gaShapeArrays.length == gaSidetabs.length)
{
for (var x = 0; x < gaShapeArrays.length; x++)
{
gaShapeArrays[x].name = gaSidetabs[x];
}
}
else
{
Console.Writeln('Warning: gaShapeArrays and gaSidetabs are not the same length. Some names will not be assigned.');
}
// go through an array of arrays
for(var x=0; x<gaShapeArrays.length; x++)
{
// and go through the objects of each one
for(var y=0; y<gaShapeArrays[x].length; y++)
{
// if "object" is in the array
if(object == gaShapeArrays[x][y])
{
// get "sidetab" from object's array's name
var sidetab = gaShapeArrays[x].name.replace('gaShapes',''); // assumes that shapearrays will have naming convention gaShapesSidetab
// we found it, we can stop now
break;
}
}
}
Alert(sidetab);
Also glad I could figure out how to retain the format of the code block, here.

Better way to find object in array instead of looping?

Example
Link: http://jsfiddle.net/ewBGt/
var test = [{
"name": "John Doo"
}, {
"name": "Foo Bar"
}]
var find = 'John Doo'
console.log(test.indexOf(find)) // output: -1
console.log(test[find]) // output: undefined
$.each(test, function(index, object) {
if(test[index].name === find)
console.log(test[index]) // problem: this way is slow
})
Problem
In the above example I have an array with objects. I need to find the object that has name = 'John Doo'
My .each loop is working, but this part will be executed 100 times and test will contain lot more objects. So I think this way will be slow.
The indexOf() won't work because I cannot search for the name in object.
Question
How can I search for the object with name = 'John Doo' in my current array?
jQuery $.grep (or other filtering function) is not the optimal solution.
The $.grep function will loop through all the elements of the array, even if the searched object has been already found during the loop.
From jQuery grep documentation :
The $.grep() method removes items from an array as necessary so that
all remaining items pass a provided test. The test is a function that
is passed an array item and the index of the item within the array.
Only if the test returns true will the item be in the result array.
Provided that your array is not sorted, nothing can beat this:
var getObjectByName = function(name, array) {
// (!) Cache the array length in a variable
for (var i = 0, len = test.length; i < len; i++) {
if (test[i].name === name)
return test[i]; // Return as soon as the object is found
}
return null; // The searched object was not found
}
I have done sometimes "searchable map-object" in this kind of situation. If the array itself is static, you can transform in to a map, where array values can be keys and map values indexes. I assume values to be unique as in your example.
Lo-Dash (www.lodash.com) has create selection of utils for easily looping etc. Check it out!
Note: But often you really don't have to worry about looping trough array with 100 elements.
If you just want to find out if the value is there, you can use lodash's includes function like this:
var find = 'John Doo'
[{ "name": "John Doo" }, { "name": "Foo Bar" }].some(function (hash) {
if (_.includes(hash, find)) return true;
});
Documentation:
_.includes()
Array.prototype.some()
Perhaps you should use the $.grep functionality in jQuery:
var test = [{
"name": "John Doo"
}, {
"name": "Foo Bar"
}]
var find = 'John Doo'
var found = $.grep(test, function(obj){
return obj['name'] == find;
});
console.log(found);
Fiddle: http://jsfiddle.net/ewBGt/3/
The only thing you can possibly do is use build-in array methods (if available) in preference over doing the looping yourself – the filter method would be applicable here.
But I expect that JS libraries like jQuery used by sbeliv01 in his answer already check for that internally (and provide a fallback solution if these array methods are not available natively) – so don’t expect a massive performance boost.

How to get javascript object references or reference count?

How to get reference count for an object
Is it possible to determine if a javascript object has multiple references to it?
Or if it has references besides the one I'm accessing it with?
Or even just to get the reference count itself?
Can I find this information from javascript itself, or will I need to keep track of my own reference counters.
Obviously, there must be at least one reference to it for my code access the object. But what I want to know is if there are any other references to it, or if my code is the only place it is accessed. I'd like to be able to delete the object if nothing else is referencing it.
If you know the answer, there is no need to read the rest of this question. Below is just an example to make things more clear.
Use Case
In my application, I have a Repository object instance called contacts that contains an array of ALL my contacts. There are also multiple Collection object instances, such as friends collection and a coworkers collection. Each collection contains an array with a different set of items from the contacts Repository.
Sample Code
To make this concept more concrete, consider the code below. Each instance of the Repository object contains a list of all items of a particular type. You might have a repository of Contacts and a separate repository of Events. To keep it simple, you can just get, add, and remove items, and add many via the constructor.
var Repository = function(items) {
this.items = items || [];
}
Repository.prototype.get = function(id) {
for (var i=0,len=this.items.length; i<len; i++) {
if (items[i].id === id) {
return this.items[i];
}
}
}
Repository.prototype.add = function(item) {
if (toString.call(item) === "[object Array]") {
this.items.concat(item);
}
else {
this.items.push(item);
}
}
Repository.prototype.remove = function(id) {
for (var i=0,len=this.items.length; i<len; i++) {
if (items[i].id === id) {
this.removeIndex(i);
}
}
}
Repository.prototype.removeIndex = function(index) {
if (items[index]) {
if (/* items[i] has more than 1 reference to it */) {
// Only remove item from repository if nothing else references it
this.items.splice(index,1);
return;
}
}
}
Note the line in remove with the comment. I only want to remove the item from my master repository of objects if no other objects have a reference to the item. Here's Collection:
var Collection = function(repo,items) {
this.repo = repo;
this.items = items || [];
}
Collection.prototype.remove = function(id) {
for (var i=0,len=this.items.length; i<len; i++) {
if (items[i].id === id) {
// Remove object from this collection
this.items.splice(i,1);
// Tell repo to remove it (only if no other references to it)
repo.removeIndxe(i);
return;
}
}
}
And then this code uses Repository and Collection:
var contactRepo = new Repository([
{id: 1, name: "Joe"},
{id: 2, name: "Jane"},
{id: 3, name: "Tom"},
{id: 4, name: "Jack"},
{id: 5, name: "Sue"}
]);
var friends = new Collection(
contactRepo,
[
contactRepo.get(2),
contactRepo.get(4)
]
);
var coworkers = new Collection(
contactRepo,
[
contactRepo.get(1),
contactRepo.get(2),
contactRepo.get(5)
]
);
contactRepo.items; // contains item ids 1, 2, 3, 4, 5
friends.items; // contains item ids 2, 4
coworkers.items; // contains item ids 1, 2, 5
coworkers.remove(2);
contactRepo.items; // contains item ids 1, 2, 3, 4, 5
friends.items; // contains item ids 2, 4
coworkers.items; // contains item ids 1, 5
friends.remove(4);
contactRepo.items; // contains item ids 1, 2, 3, 5
friends.items; // contains item ids 2
coworkers.items; // contains item ids 1, 5
Notice how coworkers.remove(2) didn't remove id 2 from contactRepo? This is because it was still referenced from friends.items. However, friends.remove(4) causes id 4 to be removed from contactRepo, because no other collection is referring to it.
Summary
The above is what I want to do. I'm sure there are ways I can do this by keeping track of my own reference counters and such. But if there is a way to do it using javascript's built-in reference management, I'd like to hear about how to use it.
No, no, no, no; and yes, if you really need to count references you will have to do it manually. JS has no interface to this, GC, or weak references.
Whilst you could implement a manual reference-counted object list, it's questionable whether all the extra overhead (in performance terms but more importantly code complexity) is worth it.
In your example code it would seem simpler to forget the Repository, use a plain Array for your lists, and let standard garbage collection take care of dropping unused people. If you needed to get a list of all people in use, you'd just concat the friends and coworkers lists (and sort/uniquify them if you needed to).
You may interest to look into reduce functions, and array.map functions. map could be used to help identify where your collections intersect, or if there is an intersection at all. A user defined reduce function could be used like a merge (kinda like overriding the addition operator so that you can apply operation to objects, or merge all collections on "id" if that is how you define your reduce function - then assign the result to your master reference array, I recommend keeping a shadow array that holds all of the root object/values in case you would like to REWIND or something). Note: one must be careful of prototype chains when reducing an object or array. The map function will be very helpful in this case.
I would suggest not to remove the object or record that is in your Repository as you may want to reference it again later. My approach would be to create a ShadowRepository that would reflect all records/objects that have at least one "Reference". From your description and code presented here it appears you are initializing all of the data and storing reference to 1,2,4,5 as appears in your code.
var contactRepo = new Repository([
{id: 1, name: "Joe"},
{id: 2, name: "Jane"},
{id: 3, name: "Tom"},
{id: 4, name: "Jack"},
{id: 5, name: "Sue"}
]);
var friends = new Collection(contactRepo,[
contactRepo.get(2),
contactRepo.get(4)
]);
var coworkers = new Collection(contactRepo,[
contactRepo.get(1),
contactRepo.get(2),
contactRepo.get(5)
]);
From the initialization of the Repository and the collections, what you are asking "Remove item from repository if there are no references to it" item 3 would need to be removed immediatly. You can however track the references in a few different ways.
I have considered using Object.observe for a similar situation. However, Object.observe does not work in all browsers. I have recently turned to WatchJS
I am working on understanding the code behind Watch.JS to allow a list of observers on an object to be created dynamically this would allow one to also remove an item that is no longer watched, though I suggest to remove the reference at the point of access - What I mean is a variable that shares the immediate lexical scope with an object that has given a single point of reference to it's sibling can be removed making it no longer accessable outside of the object that had exposed the record/item/property/object of it's sibling. With the reference that all of your other references depended on removed access to the underlying data is stopped. I am generating unique id for origin references to avoid accidentally reusing the same one.
Thank you for sharing your question and the structure you are using, it has helped me consider one of my own specific cases where I was generating uniquely identified references to a lexical sibling these unique ids were kept on the ONE object that had scope, After reading here I have reconsidered and decided to expose only one reference then assign that reference to a variable name where ever it is needed such as in creating a watcher or observer or other Collection.

Categories