Custom sort function in AngularJS - javascript

How to make a custom function to order?
I made this plunker, and noticed that I get an element at a time, which prevents me from ordering the elements of an array. Any idea?
I got to see these two issues, but none helped:
angularjs custom sort function in ng-repeat e AngularJS: Custom Sorting Function using OrderBy

You need to return a value from your function that will be compared against the other values.
For example, it looks like in your plunkr you want to sort by birthday.
You might accomplish this with a function like so:
$scope.customSortFunc = function(person) {
return new Date(person.birthday);
};
Keeping in mind that different browsers support parsing different date formats so ideally you'd use something like momentjs.
Example Plunkr

Related

How can I remove elements of a nested array and retain original structure?

I am working on a new Angular Application and I am building a dropdown navigation. I am currently using a Factory to return the array of nav items within the controller like so:
var sections = this.NavMenusFactory.sections;
The structure of the menu looks like so:
I need to cycle through the array and remove any items that the user does not have permissions to access but also retain the original structure. This is dictated by the current user and the requiredPermissions index within the array objects.
I have access to the lodash library so have been playing around with this by trying out filter and remove functions. Nothing is working exactly how I need it. To give a quick example of how I am trying to do it:
var sections = this.NavMenusFactory.sections;
this.dropdownNavItems = [];
forEach(sections, (section) => {
if (section.section === 'financials') {
this.dropdownNavItems = this.dropdownNavItems.concat(section.pages.filter(navitem => {
// console.log("section", navitem);
return !navitem.requiredPermissions || this.Permissions.parseAccessByValue(selectedVehicle, navitem.requiredPermissions);
}));
}
});
I'm not able to retain the original structure like in the image just with the items removed. I am very new to Javascript so some guidance would be much appreciated.
Your code looks correct to me, and I would suggest that your issue is actually more likely an angular related one.
Assuming you are using ng-repeat to iterate over all the results, chances are that your issue involves the 'track by' part of ng-repeat. Please see this part of the ng-repeat docs You could try using track by item.name or something similar, see if that solves your problems.

Pass index to custom orderBy function

This might be simple and stupid but I can't seem to figure it out. I have created a ng-repeat structure that I use multiple times on different objects. Basically, I iterate through elements in an array inside those objects and depending on some variable I either need to apply custom orderBy, or leave it in the original order.
So I've created that custom orderBy like that:
<li ng-repeat="tag in filter.tags | orderBy: customSort(filter.customSort)">
Basically, filter object contains array of tags and a boolean variable that states if custom sort needs to be applied. Within my customSort function I check if passed on parameter is true and perform that custom sort. But if it's false - I cannot seem to be able to output tags in their original order in that array.
I cannot pass $index because at that point it is always 0, it only becomes index inside that ng-repeat tag. I cannot do something like that filter.tags.indexOf(tag) because tag doesn't exist yet.
I'm kinda stuck, is there a way to apply orderBy conditionally?
Further explaination:
I iterate over an array of tags and print their localized values, some of those tags should be in their original order, some should be ordered alphabetically in their localized version. So my stripped custom sort does something like this:
$scope.customSort = function(language) {
return function(tag) {
return $scope.tags.[language][tag];
}
}
So far this is the only solution I came up with, I will wait a little to see if anyone answers with a better one before I mark this one as correct.
I had to pass the entire array to the sorting function, along with boolean variable used to determine whether original order or custom sort is desired and then used indexOf like this:
$scope.customSort = function(perform, tags, language) {
return function(tag) {
if (!perform) return tags.indexOf(tag);
return $scope.tags.[language][tag];
}
}

Underscore JS, Grouping by an array Attribute.

I'm trying to use Javascript to group an array by one of it's attributes.
Essentially I have a list of e-mail templates, and I'm trying to group them by the category that they are in so that I can make a collapsible accordion with it later.
I think I might have the syntax of underscore JS wrong, or I'm addressing my array incorrectly. Currently I am calling groupby using the following command:
console.log(_.groupBy(result, 'im_category'));
but my array looks like the 'im_category' property is hidden under the attributes function. I'm not sure how to get there.
I've attached my console.log of what the array looks like and what happens when I run that command. (I get three different objects when I should get 2 if it's working correctly. )
Your im_category is a property of the attributes object in your businessEntity - _.groupBy is looking for properties of businessEntity. You need to create a function as iteratee:
var grouped = _.groupBy(result, function (item) {
return item.attributes.im_category;
});
http://jsfiddle.net/jwnzh8w0/

Ember GroupableMixin by using groupProperty

I am trying to implement GroupableMixin (https://github.com/ahawkins/ember.js/blob/groupable-mixin/packages/ember-runtime/lib/mixins/groupable.js) on EmberJS but I have trouble using it.
I want to specify a groupProperty in my ArrayController (just like how we have sortProperties) and I want my groupings to be calculated based on that property.
I did try http://emberjs.jsbin.com/cawaq/3/edit and this is what I got:
App.UsersIndexController = Ember.ArrayController.extend({
sortProperties: ['created_at'],
groupProperty: 'role',
groupedContent: groupBy('role'), //How do it reference groupProperty in groupBy?
//How can groupedContent be updated whenever groupProperty is changed?
});
I have seen http://discuss.emberjs.com/t/ember-enumerable-no-group-by/3594/6 and http://jsbin.com/ukagip/2/edit but still cannot quite figure out how to make that work properly.
The intent and behavior of Mr. Hawkins' GroupableMixin is very different from the one that's used discussed in Ember's Discourse forum. It seems that you're confusing the two methods.
Mixin Approach (a la Mr. Hawkins)
Note this is the approach I'd recommend
The GroupableMixin is an instance of Ember.Mixin, which is used to extend an object's prototype. For a more in depth explanation of mix-in's see Coding Value's explanation.
We can determine the mix-ins requirements and behavior from reading the tests for the mix-in:
the class must behave like an ArrayProxy (so ArrayController's are fine)
a groupBy property must be set for the class
So we can include this mix-in in an array controller as follows:
App.UsersIndexController = Ember.ArrayController.extend(Ember.GroupableMixin, {
groupBy: 'role'
});
Now that we've satisfied the mix-in's requirements, we'll have access to a few new computed properties and functions such as groupedContent for free. Without adding anything else to the controller, we could write something like this in the template to access the groups:
{{#each group in groupedContent}}
<h1>{{group.name}}</h1>
<ul>
{{#each user in content}}
<li>{{user.name}}</li>
{{/each}}
</ul>
{{/each}}
Here's an example that groups words in an array controller by first letter.
Computed Helper Approach (a la Ember Sherpa)
This method creates a helper which defines a computed property based on the key provided to the function. We can create a similar function which maps to the group interface of the mix-in:
Sherpa = {};
Sherpa.groupBy = function (groupBy) {
var dependentKey = 'content.#each.' + groupBy;
return Ember.computed(dependentKey, function(){
var result = [];
this.get('content').forEach(function(item){
var hasGroup = !!result.findBy('name', get(item, groupBy));
if (!hasGroup) {
result.pushObject(Ember.Object.create({
name: get(item, groupBy),
content: []
}));
}
result.findBy('name', get(item, groupBy)).get('content').pushObject(item);
});
return result;
});
};
Now we can use this function to create a groupedContent computed property: groupedContent: Sherpa.groupBy('role')
Here's an example which uses the same template as the previous example and has only swapped the mix-in with this method.
Recommendation
Use the GroupableMixin, it's far more robust than the method discussed in the forum:
The method discussed in the forum...
is simple and easy to understand
is inefficient
Recomputes the entire groups array whenever a change is made to the array or relevant property
Doesn't implement map structures for lookups and instead searches iteratively through the array
Nukes all itemControllers on any computed change: If you check some boxes in the second example and then add a new word, all of the checkboxes will be cleared
The mix-in...
is a much more robust, albeit complex, implementation
handles inserts and removals to groups instead of recomputing the entire map
backs the groups with a map for better performance and efficiency
behaves as expected when using an itemController: just tick of checkboxes and add some words in the first example, and you'll notice that the checkboxes are never cleared

Filter for multiple discrete values in crossfilter

Does anyone have an approach to filtering a crossfilter object dimension on multiple values? Something like
.filterExact(["cash","visa"])
or
.filter(["cash","visa"])
...but not the range form of it...
or
.filterAll(["cash","visa"])
...but without the clearing part.
or an equivalent workaround/approach not using
.filterRange(["cash","visa"])
??
Or am I missing something in the API?
Thanks!
Larry
I was facing a similar problem. The way I solved it was that I wrote a filter function that would check whether the dimension lies in a particular array or not.
// Array of things you want to filter
var f = ["cash", "visa"];
// Assuming "dim" is our dimension
dim.filter(function(d){
return f.indexOf(d) > -1;
});
This will check if the value lies in that array and filter accordingly.
Hope this helps.
The following pull request looks like it would address your needs, but it has not yet been merged in.
Multiple arguments to filter result in union of filter operations
Once merged you would be able to do something like the following.
data.total.filter("cash", "visa");
Which would result in the union of all filter criteria.
There doesn't seem to be anything in the API, but if you want to avoid filterRange, you could to it with two basic filters and concating the results:
var paymentsByType = payments.dimension(function(d) { return d.type; }),
cashAndVisaPayments = Array.prototype.concat(paymentsByType.filter('cash').top(Infinity),paymentsByType.filter('visa').top(Infinity))

Categories

Resources