I'm taking my first steps with Backbone.js, and one of those involves being able to remove an item from a collection, and more importantly, retrieve that item. The Backbone.Collection.remove method simply returns the original collection with the item removed, so at the moment I'm obtaining a reference to the desired item prior to removal:
var Collection = Backbone.Collection.extend(...array of Backbone.Models...),
removedItem = Collection.get(3);
console.log(Collection.remove(3));//same collection sans #3
My question is if there is a short hand method for retrieving the remove item?
Edit: JFTR, I've read a fair bit of the source, and know that the original method returns a reference to the collection -
remove: function(models, options) {
// <snip for brevity>
// chain pattern incoming
return this;
},
It seemed odd to me that it didn't return the removed item., so I was just wondering if there was another method I'm missing, or a common way of achieving this pattern. Wouldn't be the first time I've used a long workaround when the API had some secret doohickey up it's sleeve...as it is I'll probably extend the class.
You could add a function to the Backbone.Collection 'type' and use removeModel on every collection you create.
Backbone.Collection.prototype.removeModel(model) {
var _model = this.get(model);
this.remove(item);
return _model;
}
var removedModel = collection.removeModel(model);
Related
Someone shared this beautifully elegant way to create a linked list from an array.
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({next, value}), null);
console.log(list);
}
let l = [3, 1, 2, 3, 4, 5];
let k = 3;
removeKFromList(l, k);
It's pretty easy to iterate arrays (for, filter, map, reduce, etc.) but a linked list has none of those features. I need to iterate through to remove k values from the list. I'm guessing I need to create a variable for the current node and use a while loop, but there's no documentation on this. I've seen some repl code doing it but it seems unnecessarily complicated.
How do you iterate through a linked list in javascript?
First of all, although the idea of using reduce is indeed beautiful, I must say that the result is not so good, because the resulting nodes have a field "next" where the value is and a field "value" where the next is, i.e. they are swapped. So let's fix that:
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({value: next, next: value}), null);
console.log(list);
}
Secondly, the name of that function is awful, it should be named "arrayToLinkedList" or something more suggestive. Also, logging the result does not make sense, we should return it instead. Moreover, the parameter k is simply unused. Fixing those things:
function arrayToLinkedList(array) {
return array.reduceRight((prev, cur) => ({ value: cur, next: prev }), null);
}
Now, let's work on how to iterate over this. What do you think? I will give some hints because giving the answer directly probably won't help you learn much:
Observe that the linked list itself is:
Either the value null, or
A plain object with two fields, one field "value" which can be anything, and one field "next" which is a linked list.
Observe that the above (recursive) definition is well-defined and covers all cases.
Now, use that to your assistence. How do you get the first value? That would be simply myList.value, right? Now how do I get the second value? By applying .next, we get the next linked list, and so on. If next is null, you know you have to stop.
Let me know if you need further assistance.
EDIT: I noticed that you're looking for the right way to make an iterating method on lists with an idea of "adding it to the prototype" or something. So, about that:
To add an instance method by a prototype, you'd need your linked lists to be a class. That would be overcomplicating, unless you really have a good reason to define a class for this (which would be creating several methods and utility things around it).
It is much better, in my opinion, to just define a function that takes one linked list as the first parameter and a callback function as the second parameter, similarly to lodash's .each:
function forEachValueInLinkedList(linkedList, callback) {
// here you loop through all values and call
// callback(value)
// for each value.
}
I'd say this would be the most "javascriptonic" way to do it.
I'm creating a game bot on telegram using node js.
Currently I'm facing a problem on shared variable (module.exports). I'm storing some of the data on the variable. And the problem is, the shared variable index always change. For example, please refer to my code below
var sharedVar = [];
createNewRoom = function(res) {
var index = sharedVar.length;
sharedVar.push({ groupId : res.chat.id }); // every time this function is invoked, it will create a new array inside sharedVar object
//Here comes the problem, it's about the index,
//because I'm using sharedVar to store arrays, then it will become a problem,
//if one array is deleted (the index will change)
var groupId = sharedVar[index].groupId; // it runs OK, if the structure of array doesn't change, but the structure of array change, the index will be a wrong number
}
As you can see, i got callGameData function, when i call it, it will show the last value of sharedVar, it's supposed to show the current room values / data.
As i mention on the code above, it's all about the dynamic array in the sharedVar object, the index will change dynamically
Any thoughts to tackle this kind of issue? I was thinking about using a new sharedVar object everytime the createNewRoom function is invoked, but the thing is, i have to use sharedVar in many different function, and i still can't figure it out on using that method.
EDIT
This is the second method
var gameData = undefined;
createNewRoom = function() {
this.gameData = new myConstructor([]); // it will instantiate a new object for each new room
}
myConstructor = function(data) {
var _data = data;
this.object = function() {
return _data;
}
}
callGameData = function() {
console.log(gameData);
}
An array is fundamentally the wrong data type to use if you want to keep indices the same even in the face of removing entries.
A better method is to use properties of an object. For example:
var roomCache = { nextId: 1 };
createNewRoom = function(res) {
roomCache[roomCache.nextId++] = {groupId: res.chat.id}; // Add a new object to the cache and increment the next ID
}
After adding two elements, you'll have the rooms in roomCache[1] and roomCache[2] - if you want to start at zero just change the original value of nextId. You can delete elements in this object and it won't shift any keys for any other objects - just use delete roomCache[1] for example to get rid of that entry.
This assumes there isn't a better ID out there to use for the cache - if, for example, it made more sense to lookup by res.chat.id you could certainly use that as the key into roomCache rather than an auto-incrementing number. Here's how it would look like to cache the values by the group ID instead:
var roomCache = { };
createNewRoom = function(res) {
roomCache[res.chat.id] = {groupId: res.chat.id}; // Assumes res.chat.id is not a duplicate of an already cached obhect
}
Now you could just look up by group ID in the cache.
Yes, it's definitely a problem cause you are not keeping track of the index in a logical way, you are relying on position on the array which it changes, you need something that doesn't change over time to keep consistency and supports deletition of the element without affecting the rest of the elements. You could use mongo to store the generated rooms by id or maybe redis or some kind of key value pair database to store that kind of information.
I want to add attribute to a JS object, but in a custom place, After a given attribute.
var me = {
name: "myname",
age: "myage",
bday: "mybday"
};
me["newAt"] = "kkk"; //this adds at the end of the object
Is there a way to specify the object (me), an attribute(age) in it and add a new attribute(newAt) right after the specified one? A better way than doing string operations?
var newMe = {
name: "myname",
age: "myage",
newAt: "newAttr",
bday: "mybday"
}
UPDATE: (Since people are more focused on why I'm asking this than actually answering it)
I'm working on a drawable component based on user input - which is a JS object. And it has the ability to edit it - so when the user adds a new property based on "add new node" on the clicked node, and I was thinking of adding the new node right after it. And I want to update the data accordingly.
JavaScript object is an unordered list of properties. The order is not defined and may vary when using with an iterator like for in. You shouldn't base your code on the order of properties you see in debugger or console.
JavaScript objects do, as of ES2015, have an order to their properties, although that order is only guaranteed to be used by certain operations (Object.getOwnPropertyNames, Reflect.ownKeys, etc.), notably not for-in or Object.keys for legacy reasons. See this answer for details.
But you should not rely on that order, there's no point to it, it's more complicated than it seems initially, and it's very hard to manipulate (you basically have to create a new object to set the order of its properties). If you want order, use an array.
Re your edit:
I'm working on a drawable component based on user input - which is a JS object. And it has the ability to edit it - so when the user adds a new property based on "add new node" on the clicked node, and I was thinking of adding the new node right after it. And I want to update the data accordingly.
The best way to do that is, if you want a specific order, keep the order of keys in an array and use that to show the object.
While you could use ES2015's property order for it, to do so you'd have to:
Require your users use a truly ES2015-compliant browser, because this cannot be shimmed/polyfilled
Destroy the object and recreate it adding the properties in the specific order you want each time you add a property
Forbid properties that match the specification's definition of an array index
It's just much more work and much more fragile than keeping the order in an array.
The simplest solution I could find was to iterate through the keys of the parent and keep pushing them to form a clone of the parent. But to additionally push the new object if the triggered key is met.
var myObj = {
child1: "data1",
child2: "data2",
child3: "data3",
child4: "data4"
};
var a = (function addAfterChild(data, trigChild, newAttribute, newValue) {
var newObj = {};
Object.keys(data).some(function(k) {
newObj[k] = data[k];
if (k === trigChild) {
newObj[newAttribute] = newValue;
}
});
return newObj;
})(myObj, "child3", "CHILD", "VALUE");
document.getElementById("result").innerHTML = JSON.stringify(a);
<p id="result"></p>
I'm working with several backbone collections and sometimes I need to access parts of them based on some criteria.
METHOD 1
As already stated in this question, using filter() on the collection itself returns an array of models and not another collection. This can work in simple cases, but it has the effect of losing collection's method concatenation as a plain array of models won't have all methods defined in the collection.
METHOD 2
The answer to that question suggested creating a new collection passing the array of models to the constructor. This works but has the side effect of calling the collection's constructor every time, so any event binding that might be defined there gets stacked on every time you filter the collection.
So what's the correct way to create a sub-collection based on some filter criteria?
Should I use method 1 and create more filtering methods instead on relying on method chaining?
Should I go with method 2 and avoid binding events in the collection's constructor?
Personally I would create more filtering methods on the collection, because it has the additional benefit of encapsulating logic inside the collection.
You could also try to reuse the existing collection. I was toying around with the idea, and arrived at something like this:
var Collection = Backbone.Collection.extend({
//Takes in n arrays. The first item of each array is the method you want
//to call and the rest are the arguments to that method.
//Sets the collection.models property to the value of each successive filter
//and returns the result of the last. Revers the collection.models to its original value.
chainFilters: function(/*args..*/) {
var models = this.models;
try {
filters = _.toArray(arguments);
_.each(filters, function(filter) {
this.models = filter[0].apply(this, _.rest(filter));
}, this);
} catch(err) {
this.models = models;
throw err;
}
var filtered = this.models;
this.models = models;
return filtered;
}
});
Usage:
var results = collection.chainFilters(
[ collection.filter, function(model) { return model.get('name') === 'foo'; } ],
[ collection.someMethod, 'someargument' ],
[ collection.someOtherMethod ]
);
Here's a working sample. It's a bit peculiar, I know.
It depends on the use case. If you want those models to update a view then you probably want a new collection as otherwise you don't get the nice reactive template updates. If you simply wanted the models to iterate through or manipulate data without worrying about the data updating then use the array + underscore.js.
Try it with the arrays and if you find yourself writing a lot of boiler plate code with features already in a collection but not in underscore.js, just start using a collection.
I'm making a Google Chrome Extension that uses context menus as its main UI. Each menu item triggers the same content script, but with different parameters. What I basically did is store every item (and its corresponding data) in the form of a JSON object that has the following form :
{name, parent_id, rule_number, meta_array[], childCount}
name, child_count and parent_id are used to create the hierarchy when the context menus are built. The data that's passed to the script is rule_number (int) and meta_array (array of strings). All of these objects are stored into an array called indexData[].
When a menu item is clicked, the id provided is just used as an index in the "indexData" array to get the right data and pass it to the script.
For example:
// Iterates through the objects
for(var j = 0; j < objectsArray.length; j++) {
// Context menu created with unique id
var id = chrome.contextMenus.create({
"title": objectArray[j].name,
"onclick": injectScript,
"parentId": objectsArray[j].parent_id });
// Stores the objects at the corresponding index
indexData[id] = objectsArray[j]; }
Now, there was a particular large set of data that comes back often. Instead of listing every single of these elements every time I wanted them as part of my menu, is just added a boolean parameter to every JSON object that needs this set of data as its children. When the menus are created, a function is called if this boolean is set to true. The script then just iterates through a separate list of objects and makes them children of this parent object. The created children even inherit certain things from the parent object.
For example, if a parent object had a meta_array like such ["1", "2", "3", "4"], its children could all look like so ["1", "2", custom_children_data[3], "4"].
The problem is that this last part doesn't work. While the children are created just fine and with the right name, the data that's associated with them is wrong. It's always going to be the data of the last object in that separate list. This is what the function looks like:
// Iterate through children list
for(var i = 0; i < separateList.length; i++){
// Just copying the passed parent object's data
var parentData = data;
var id = chrome.contextMenus.create({
"title": separateList[i].name, // Get item [i] of the children list (works fine)
"onclick": injectScript,
"parentId": parentId // Will become a child of parent object
});
// Trying to change some data, this is where things go wrong.
parentData.meta[2] = separateList[i].meta;
// Save in indexData
indexData[id] = parentData; }
On the loop's first iteration, parentData.meta[2] gets the right value from the list and this value is thereafter saved in indexdata. But on subsequent iterations, all the values already present in indexData just get swiped away and replaced by the latest data being read from the list. When the last value is read, all the newly added elements in indexData are therefore changed to that last value, which explains my problem. But why on earth would it do that ? Does Java somehow treat arrays by address instead of value or something in this case ?
Maybe I'm missing something really obvious, but after many attempts I still can't get this to work properly. I tried to be as specific as possible in my description, but I probably forgot to mention something, so if you want to know anything else, just ask away and I'll be happy to provide more details.
Thanks.
The problem would be indexData[id] = parentData where you are making indexData[id] a reference to parentData, and then modifying parentData on the next iteration of your loop.
Since parent data is not a simple array (It contains at least one array or object), you cannot simply use slice(0) to make a copy. You'll have to write your own copy function, or use a library which has one.
My guess is that this is where your problem lies:
// Just copying the passed parent object's data
var parentData = data;
This does not, in fact, copy the data; rather, it creates a reference to data, so any modifications made to parentData will change data as well. If you're wanting to "clone" the data object, you'll have to do that manually or find a library with a function for doing so.