This theoretically seems kind of easy but I am not 100% on the appropriate code to write.
Inside a view if I am defining a max variable that returns an attribute points in a collection, I can pass that variable as an object into my template like maxPoints.toJSON() and get that object's attributes.
var maxPoints = this.collection.max(function(player){
return player.get('team') == home || player.get('team') == away ? player.get('points') : 0;
});
//Pass to template
this.$el.find('.top-preformer').append(this.template(maxPoints.toJSON()));
//Inside the template using underscore.js I can get the `name` of the object returned
<%= name %>
//Easy, but now I am passing two objects, how do I access `name` for top points
//or `name` for top assists?
This is fine, but what if I want to retrieve multiple max values and interact with them inside the template?
var maxAssists = this.collection.max(function(player){
return player.get('team') == home || player.get('team') == away ? player.get('assists') : 0;
});
Now I have an object maxAssists that I could pass into my template, but I am not sure how this would work. How could I interact with each specific object?
I'm sorry if this answer isn't appropriate now that you've deleted your previous question and asked another one. This is the answer I was about to send when you deleted your question:
I hope I've correctly understood your scenario. Let me sum it up just in case:
There's a Model, let's call it Player, which represents a player with a name, a team, points and assists (at least). There's a Collection of Players which is held by the View you're referring to. This View shows stuff about players and also highglights the player with most points and the one with most assists.
I'm assuming too much about your needs, but let's say you want to show a list of players with their info and then the info of those highlighted. Check this JSFiddle I prepared to show my approach.
As a rule of thumb, you shouldn't set data in the DOM unless it's extrictly necessary, that's why you're using JavaScript after all. Since you have a View which holds a Collection, that View should be the one to hold references to both most points player and most assists player. Using your max functions, you may do it this way:
getMaxPointsPlayer: function () {
return this.getMax("points");
},
getMaxAssistsPlayer: function () {
return this.getMax("assists");
},
getMax : function (attribute) {
return this.collection.max(function (player) {
return player.get("team") === this.homeTeam || player.get("team") === this.awayTeam ? player.get(attribute) : 0;
}, this);
},
Note two things:
You should always use === instead of == where possible. Search this site for more info on it.
Since I don't know where those home and away variables are in your code and what are them, I placed them as member variables in the view (and they're just strings).
So, when you want to reference the player with most points/assists, you'd call one of those functions which are part of the View. Then you may store it in a variable (so you can get it without further filtering) or use it straight away from the call to the function.
If you study the code in the JSFiddle, you'll see more stuff than you asked for. It's there to show you a nice way to keep your application clean and tidy (obviously, it's just a JSFiddle). Try to keep Views on their own business (may it be present and deal with a Model, have a list of sub-views and manage them because they're part of a Collection or just be a manager of other sub-views). That way, those views have the logic they need without dealing with data in the DOM.
Related
Set and Map both are newer data types in es6 and for certain situations both can be used.
e.g - if i want to store all unique elements, i can use Set as well as Map with true as value.
const data: string[] ;
// console.log('data', data[0])
const set = new Set();
const map = new Map<string, boolean>();
data.forEach((item) => {
map.set(item, true);
});
data.forEach((item) => {
set.add(item);
});
Both works, but i was wondering which one is faster ?
Update 1
I am looking for which of the data structure is faster in case of storing data.
checking if value exist using -
map.has(<value>)
set.has(<value>)
deleting values
Also i can understand true is redundant and not used anywhere, but i am just trying to show how map and set can be used alternatively.
What matters is speed.
In the most basic sense:
Maps are for holding key-value pairs
Sets are for holding values
The true in your map is completely redundant ... if a key exists, that automatically implies, that it is true/exists - so you will never ever need to use the value of the key-value pair in the map (so why use the map at all, if you're never gonna make use of what it is actually for? - to me that sounds like a set/array with extra steps)
If you just want to store values use an array or set. - Which of the two depends on what you are trying to do.
The question of "which is faster" can't really be answered properly, because it largely depends on what you are trying to do with the stored values. (What you are trying to do also determines what data structure to use)
So choose whatever data structure you think fits your needs best, and when you run into a problem that another would fix, you can always change it later/convert from one into another.
And the more you use them and the more you see what they can and can not do, the better you'll get at determining which to use from the start (for a given problem)
How it is
I have an array of objects called vm.queued_messages (vm is set to this in my controller), and vm.queued_messages is used in ng-repeat to display a list of div's.
When I make an API call which changes the underlying model in the database, I have the API call return a fresh list of queued messages, and in my controller I set the variable vm.queued_messages to that new value, that fresh list of queued messages.
vm.queued_messages = data; // data is the full list of new message objects
The problem
This "full replacement" of vm.queued_messages worked exactly as I wanted, at first. But what I didn't think about was the fact that even objects in that list which had no changes to any properties were leaving and new objects were taking their place. This made no different to the display because the new objects had identical keys and values, they were technically different objects, and thus the div's were secretly leaving and entering every time. THIS MEANS THERE ARE MANY UNWANTED .ng-enter's AND .ng-leave's OCCURRING, which came to my attention when I tried to apply an animation to these div's when they entered or left. I would expect a single div to do the .ng-leave animation on some click, but suddenly a bunch of them did!
My solution attempt
I made a function softRefreshObjectList which updates the keys and values (as well as any entirely new objects, or now absent objects) of an existing list to match those of a new list, WITHOUT REPLACING THE OBJECTS, AS TO MAINTAIN THEIR IDENTITY. I matched objects between the new list and old list by their _id field.
softRefreshObjectList: function(oldObjs, newObjs) {
var resultingObjList = [];
var oldObjsIdMap = {};
_.each(oldObjs, function(obj) {
oldObjsIdMap[obj._id] = obj;
});
_.each(newObjs, function(newObj) {
var correspondingOldObj = oldObjsIdMap[newObj._id];
if (correspondingOldObj) {
// clear out the old obj and put in the keys/values from the new obj
for (var key in correspondingOldObj) delete correspondingOldObj[key];
for (var key in newObj) correspondingOldObj[key] = newObj[key];
resultingObjList.push(correspondingOldObj);
} else {
resultingObjList.push(newObj);
};
});
return resultingObjList;
}
which works for certain things, but with other ng-repeat lists I get odd behavior, I believe because of the delete's and values of the objects being references to other controller variables. Before continuing down this rabbit hole, I want to make this post in case I'm thinking about this wrong, or there's something I'm missing.
My question
Is there a more appropriate way to handle this case, which would either make it easier to handle, or bypass my issue altogether?
Perhaps a way to signal to Angular that these objects are identified by their _id instead of their reference, so that it doesn't make them leave and enter as long as the _id doesn't change.
Or perhaps a better softRefreshObjectList function which iterates through the objects differently, if there's something fishy about how I'm doing it.
Thanks to Petr's comment, I now know about track by for ng-repeat. It's where you can specify a field in your elements that "identifies" that element, so that angular can know when that element really is leaving or entering. In my case, that field was _id, and adding track by message._id to my ng-repeat (ng-repeat="message in ctrl.queued_messages track by message._id") solved my issue perfectly.
Docs here. Search for track by.
We can use dojo.query to get certain elements based of CSS selectors but how do we query on object types?
For example, get all the TextBox elements on the page and then use dojo.connect to bind a function?
This is not completely supported, yet there are two ways of doing it as i see it.
One, figure out which is the unique class for a TextBox (.dijitTextBox), call dojo.query('.dijitTextBox'), loop result dojo.forEach and get the widget with dijit.getEnclosingWidget(domnode)
var textboxArray = [];
dojo.forEach(dojo.query('.dijitTextBox'), function(domnode) {
textboxArray.push(dijit.getEnclosingWidget(domnode));
});
Or two, loop the dijit.registry._hash, test declaredClass, if its dijit.form.TextBox - connect.
var textboxArray = dojo.filter(dijit.registry._hash, function(widget) {
return widget.declaredClass && widget.declaredClass == 'dijit.form.TextBox';
})
Depending your setup, choose the most efficient one. The latter is commonly best - unless you have 100's of widgets in your page. The first will have to xpath all your elements of the page. Allthough, remember that dojo.query takes a second parameter as 'parentNode'
I am quite new to CouchDB and have a very basic question:
Is there any possibility to pass a variable from the client into the map function, e.g.:
function (doc, params) {
if (doc.property > params.property) emit(doc, null);
}
Thanks for your help,
Christian
While Dominic's answer is true, the example in the actual question can probably be implemented as a map function with an appropriate key and a query that includes a startkey. So if you want the functionality that you show in your example you should change your view to this:
function(doc) {
if( doc.property )
emit( doc.property, null);
}
And then your query would become:
/db_name/_design/view_doc/_view/view_name?startkey="property_param"&include_docs=true
Which would give you what your example suggests you're after.
This is the key (puns are funny) to working with CouchDB: create views that allow you to select subsets of the view based on the key using either key, keys or some combination of startkey and/or endkey
No, map functions are supposed to create indexes that always take the same input and yield the same output so they can remain incremental. (and fast)
If you need to do some sort of filtering on the results of a view, consider using a _list function, as that can take client-supplied querystring variables and use them in their transformation.
I'm playing around with the idea of creating a global search that allows me to find any model in any of a number of collections by any of the model's attributes. For example:
I have the following collections:
Users
Applications
Roles
I don't know ahead of time what attributes each User, Applicaion and Role will have but for illustration purposes lets say I have:
User.name
User.last_name
User.email
Application.title
Application.description
Role.name
Role.description
Now, lets say I create a model called Site with a method called search. I want Site.search(term) to search through all the items in each collection where term matches any of the attributes. In essence, a global model search.
How would you suggest I approach this? I can brute-force it by iterating through all the collections' models and each model's attributes but that seems bloated and inefficient.
Any suggestions?
/// A few minutes later...
Here's a bit of code I tried just now:
find: function(query) {
var results = {}; // variable to hold the results
// iterate over the collections
_.each(["users", "applications", "roles"], _.bind(function(collection){
// I want the result to be grouped by type of model so I add arrays to the results object
if ( !_.isUndefined(results[collection]) || !_.isArray(results[collection]) ) {
results[collection] = [];
}
// iterate over the collection's models
_.each(this.get(collection).models, function(model){
// iterate over each model's attributes
_.each(model.attributes, function(value){
// for now I'm only considering string searches
if (_.isString(value)) {
// see if `query` is in the attribute's string/value
if (value.indexOf(query) > -1) {
// if so, push it into the result's collection arrray
results[collection].push(model);
}
};
});
});
// a little cleanup
results[collection] = _.compact(results[collection]);
// remove empty arrays
if (results[collection].length < 1) {
delete results[collection];
}
},this));
// return the results
return results;
}
This yields the expected result and I suppose it works fine but it bothers me that I'm iterating over three arrays. there may not be another solution but I have a feeling there is. If anyone can suggest one, thank you! Meanwhile I'll keep researching.
Thank you!
I would strongly discourage you from doing this, unless you have a very limited set of data and performance is not really a problem for you.
Iteration over everything is a no-no if you want to perform search. Search engines index data and make the process feasible. It is hard to build search, and there is no client-side library that does that effectively.
Which is why everybody is doing searching on the server. There exist easy (or sort of) to use search engines such as solr or the more recent and my personal preference elasticsearch. Presumably you already store your models/collections on the server, it should be trivial to also index them. Then searching becomes a question of making a REST call from your client.