Sorting with Angularjs OrderBy in special cases - javascript

I have some data which I'm attempting to sort in a table using Angular's orderBy filter. Here's the data I'm attempting to sort.
[
{
"name":"John Doe",
"room":"1M-103",
},
{
"name":"Joe Schmoe",
"room":"12M-353",
},
{
"name":"Bob Guy",
"room":"13M-546",
},
{
"name":"Another Person",
"room":"12M-403",
},
{
"name":"Fred No-name",
"room":"3M-204",
},
[
So sorting by name works just fine. Sorting by room, however, is another issue. I'm sorting it the exact same way. Here's what I would like it to look like after sorting:
1M-103
3M-204
12M-353
12M-403
13M-546
How it actually looks after the sorting is this:
12M-353
12M-403
13M-546
1M-103
3M-204
Anyone able to help? I know why it's sorting like that, because it's going digit by digit and sorting it correctly, but is there some way that someone has found to get some slightly better sorting?

You can create a custom sorter to do that.
<li ng-repeat="item in items | orderBy:mySorter">{{item}}</li>
$scope.mySorter = function (item) {
return Number(item.room.split('-')[0].replace('M', ''));
}
I also created a demo for you.
Updated
You can also use Regex:
$scope.mySorter = function (item) {
return Number(item.room.replace(/[^\d]/g, ''));
}
Demo

Actually sza's answer doesn't quite work, since it only takes into account the numbers before the letter 'M'. So everything that starts with 12M would be read as '12' and considered equal. All the 12M's would come after 11M's and before 13M's, but would then be displayed in a random order among other 12M's.
I worked up this code:
<li ng-repeat="item in items | orderBy:mySorter">{{item}}</li>
$scope.mySorter = function (item) {
return Number(item.residenceRoom.replace('M', '.').replace('-', ''));
}
It creates a decimal number based on the number before the M dot the number after the M. So 12M-353 would be '12.353' and 12M-403 would be '12.403' so they would be sorted correctly.
The only problem I see is if the room numbers aren't always 3 digits. For example a hypothetical room 1M-2 would be sorted as '1.2' while room 1M-103 would be '1.103' so a room 2 would be sorted after room 103. But you could fix this pretty easily by formatting all your rooms with 3 digits, eg. room 1M-002.

Related

How to group by in service now?

I am new in service now and I don't know yet how to manipulate the data unlike in MySQL and SQL. I just want to know how can I group by the data.
Here is my code to show the data:
var group = new GlideRecord("table");
group.addEncodedQuery('u_active=True');
group.orderBy('u_order_in_services');
group.query();
while (group.next()) {
gs.info(group.group_name);
}
The result is:
Group 1
Group 1
Group 2
Group 2
Group 3
Group 3
Needed result is:
Group 1
Group 2
Group 3
Use GlideAggregate (instead of of GlideRecord) if you need a GROUP BY.
There is an introduction here:
https://developer.servicenow.com/blog.do?p=/post/glideaggregate/
You could use GlideQuery for this, example:
var group = new global.GlideQuery('sys_user_group')
.where('active', true)
.orderBy('name')
.select('name')
.toArray(100);
GQ.jsonDebug(group)
GlideQuery API Documentation
With either GlideQuery or GlideAggregate, to get the result you want you should use the groupBy method. It's hard to tell precisely what you're doing since you didn't give us the actual table name, but, following your example, the code should look like this:
new GlideQuery('table')
.where('u_active', true)
.groupBy('group_name')
.aggregate('count')
.select()
.map(function (record) { return record.group.group_name })
.forEach(GQ.debug);
A comparable GlideAggregate example would look like this:
var ga = GlideAggregate('table');
ga.addQuery('u_active', true);
ga.groupBy('group_name');
ga.addAggregate('COUNT')
ga.query();
while (ga.next()) {
gs.debug(ga.getValue('group_name'));
}
Note, I removed the orderBy clause of both these queries since it makes less sense when we're grouping the results. Also, if this really is a custom table, I would triple-check that the group_name field isn't actually named u_group_name. If so, you'll need to update my examples to work properly.

Double sort list in react-native

I'd like to know how to "double sort" items in react native in order to obtain a double criteria sorted list.
The data I receive from BE is a list of match: every match is an object containing 2 players, a time and a competition.
I d'like to sort items by time (so that a today match is showed before a tomorrow match) and by competition (so that a more important competition is showed before a lower one).
If two or more matches are played on the same day and within the same competition, the earlier match is showed before.
A schema better shows what I mean: I'd like to customize the UI oc the day-row, the competition-row and the match-row.
The data I receive from BE are formatted in this way:
How could I obtain what I want? I've tried with sortable list but with no result.
TY in advance
The javascript docs for sort show that you can sort using an arbitrary function. I would write a function which first compares days and then compares competitions:
function compareByDayThenByCompetition(a, b) {
if (a.day < b.day) {
return -1;
}
if (a.day > b.day) {
return 1;
}
return a.competition - b.competition;
}
events = [{day:3, competition:2}, {day:3, competition:3}, {day:3, competition:1}, {day:2, competition:1}]
console.log(events.sort(compareByDayThenByCompetition))
The best way to filter such data is.
Filter the complete list and make an array of object the object should be grouped by same timestamp.
2)Now iterate the filtered object array and filter the object value.

Grouping of similar elements using filter-Angularjs

I'm trying to group and add the similar elements in an array that I'm displaying in ng-repeat..
Plunker:Group Array Items
I tried to apply filter as:
<div ng-repeat="shape in shapes|filter:groupFilter">
{{shape.name}}- {{shape.value}}
</div>
$scope.groupFilter=function(item)=>{
return item.name===item.name;
}
where I'm not able to access the whole elements at a time so that I can compare the values and add them up..
The end result that I'm expecting is like this...
Circle- 17
Rectangle- 13
edit
Updated plunkr - http://jsfiddle.net/2s9ogyh8/1/
After reading the question again I realized that you were trying to group not only visually but you also wanted to aggregate values. If you were simply trying to group existing values, then you'd use my original answer.
Since you're aggregating the totals, I'd recommend doing so before the UI renders it, otherwise you encounter the $digest loop issue from #toskv's answer. His aggregating logic is pretty much the same though.
end edit
Here is a related post on this topic - orderBy multiple fields in Angular
Take a look at the built-in orderBy filter - https://docs.angularjs.org/api/ng/filter/orderBy
If you're looking for something more than just ordering by the name/type of shpae then you most likely won't be able to do this via a declarative object matcher but you can pass your groupFilter function back to it.
Updated plunker - http://jsfiddle.net/d13zmk3y/2/
A simple way to do it would be to make filter that uses reduce to group everything.
app.filter('group', function() {
return function(items) {
return items.reduce(function(aux, current) {
if (aux[current.name]) {
aux[current.name].count += parseInt(current.value, 10);
} else {
aux[current.name] = {
name: current.name,
count: parseInt(current.value, 10)
};
}
return aux;
}, {});
};
});
You can see a fully working example here.

Creating meaningful suggestions based on an array of tags

I have a project where there is a catalogue of items, each with an array of tags. I would like to present items that are similar, based on these tags.
Something like this (but with a much larger data set):
{
"item": {
"description":"thing",
"tags": ["a","b","c","e","f"]
},
"item": {
"description":"thing",
"tags": ["a","b"]
},
"item": {
"description":"thing",
"tags": ["a","c"]
},
"item": {
"description":"thing",
"tags": ["b","c"]
}
}
Two things I have tried so far:
First was a straight intersection between the tags on the individual item, and other items that have one or more of the same tags. This works well, but in cases where a tag is somewhat generic (think, tagged with something like "music" where they are all musical items), the number of returned items is huge.
The second one was a slightly crazy idea, where I turned the array of tags into a string, and calculated the levenshtein difference. This works for items that have a length that is approximately the same or larger, but is clunky. Still, it did trim off a lot of the fat that first approach returned. It is not the right way, but wanted to show what I am aiming for. Implemented it like this:
// snip: this is inside a BB collection
getSimilarByTag: function(tags, ignore){
var hits = [];
if (tags) {
this.filter(function(item){
if (item.get('cat') === ignore){
return; // no need to include
};
var itemTags = item.get('tags');
var result = _.intersection(tags, itemTags);
if (result.length) {
// calc levenshtein distance between the intersection and the search array
var dist = _.str.levenshtein(result.join(' '), tags.join(' '));
if (Math.log(dist) < 1.5) { // this value needs tuning?
hits.push(item.toJSON());
} else {
// based on our magic number above, ignore this
}
};
});
}
return hits;
}
I'm doing all my code in javascript, using backbone and underscore. However, the language is not so important - just curious about what kind of technique or algorithm might give a better set of results.
A simple routine for most applicable data could be, return matches in order of size of tag intersection, with a limited return count. If you could weight certain tags as being more important then you could use that to adjust the returned order. For example if the user has previously bought items from the catalogue, then the tags linked to their purchases could have an increased score in the order algorithm.

angular-js filter to find an empty string

I'd like to use the filters to return all values that have an empty string for a particular field.
That is, in the code:
var groups = $filter('unique')(addresses, 'country');
var groupedByCountry = [];
angular.forEach(groups, function (value, key) {
var selectedGroup = value['country'];
var grouped = $filter('filter')(addresses, { country: selectedGroup });
this.push(grouped);
}, groupedByCountry);
As can be seen in http://jsfiddle.net/b7cjM/, it creates groups as expected where a country is specified, but I'd like the last group to contain only the addresses that have no country specified (instead of, as currently, a group of all addresses in existence).
Is this possible using angularjs?
**
As suggested by punund, I wrote a 'groupBy' filter [a naive conversion of the 'unique' filter: http://jsfiddle.net/b7cjM/1 ], which is a much simpler solution to my problem than the one I was attempting. I suspect a custom 'filterByEmpty' of this type is also the solution for the problem as presented.
underscore or lodash may work better for you:
groupedBy = _.groupBy($scope.addresses, 'country');
Empty strings are properly taken into account, and you don't need the second filter. Another option would be to implement a kind of "groupBy" angular filter yourself.

Categories

Resources