I'm working with some code I've adapted from and there's something I don't quite understand the best way to do. I'm trying to streamline a bit of code with different sorting functions that are applying sorts for specific values to an array of list items.
At the moment the function does a compare based on a specific factor and then returns the values to sort.
I want to pass two additional variables with this array/sort call but I can't seem to work out the way to write this. At the moment I'm doing it in a nasty way by having global variables on the window, but I'd rather pass the variables directly.
Based on the code below, any ways to tighten & clean it up would be appreciated:
arr = [];
sort_func = $j(this).children(':selected').val();
$j('li.collectionItem').each(function(){
arr.push(this);
});
if (sort_func == "a_z")
{
window.dataType = 'alpha';
window.bigFirst = false;
arr.sort(sort_products);
}
else if (sort_func == "z_a")
{
window.dataType = 'alpha';
window.bigFirst = true;
arr.sort(sort_products);
}
// custom sort functions
function sort_products(a, b)
{
dataType = window.dataType;
bigFirst = window.bigFirst;
var compA = $j(a).data(dataType);
var compB = $j(b).data(dataType);
if (bigFirst == true)
{
return (compA > compB) ? -1 : (compA < compB ) ? 1 : 0;
}
else
{
return (compA < compB) ? -1 : (compA > compB ) ? 1 : 0;
}
}
You can wrap original sort_products in another function, like this:
function sort_products(dataType, bigFirst)
{
return function (a, b)
{
var compA = $j(a).data(dataType);
var compB = $j(b).data(dataType);
if (bigFirst == true)
{
return (compA > compB) ? -1 : (compA < compB ) ? 1 : 0;
}
else
{
return (compA < compB) ? -1 : (compA > compB ) ? 1 : 0;
}
}
}
And then you can use it like this:
if (sort_func == "a_z")
{
arr.sort(sort_products('alpha', false));
}
else if (sort_func == "z_a")
{
arr.sort(sort_products('alpha', true));
}
I don't know how many elements you have, but it'd speed things up if you'd avoid making those jQuery (assuming that's what $j is) calls inside the comparator function.
var arr = []; // You really need to declare your variables!
var sort_func = $j(this).children(':selected').val();
var sortInfo = {
'a_z': {type: 'alpha', ascending: true},
'z_a': {type: 'alpha', ascending: false},
// ... whatever the other types are
}[sort_func];
$j('li.collectionItem').each(function(){
arr.push({ elem: this, key: $j(this).data(sortInfo.type) });
});
arr.sort(function(a, b) {
return (sortInfo.ascending ? 1 : -1) *
a.key > b.key ? 1 : a.key < b.key ? -1 : 0;
});
// transform the array into an array of just the DOM nodes
for (var i = 0; i < arr.length; ++i)
arr[i] = arr[i].elem;
Related
I have a list of items that are being rendered to the browser and I want to .sort() them in alphabetical order. I've used .sort in other contexts but I'm not sure where exactly I would place it in this one.
In my code I had placed it after .map in order to chain them together, but to no avail so I left it out. Any thoughts?
JS snippet:
import testjson from './test.json';
function loadAllCourses() {
let jsonRes = testjson.d.results.map(function(val) {
return {
"Title": val.Title
}
});
let allTitles = jsonRes;
for (var i = 0; i < allTitles.length; i++) {
$(".all-courses-ul").append("<li>"+allTitles[i].Title+"</li>") // body---to be in abc order
$(".form-control").append("<option>"+allTitles[i].Title+"</option>") // dropdown---same as above
};
} // ------------------ loadAllCourses
loadAllCourses();
You would use:
testjson.d.results.sort(function(a, b){
return (a.Title > b.Title) ? 1 : ((b.Title > a.Title) ? -1 : 0)
}).map(function(val)
I want to havesimple slickgrid column sort. however I might not understand the basic idea.
what I have done is like this.
Make column sortable
{id: "score", name: "number", field: "score",sortable: true},
Make function for sort calculation.
function sortfn(o1, o2) {
if (o1[column.field] > o2[column.field]) {
return 1;
} else if (o1[column.field] < o2[column.field]) {
return -1;
}
return 0;
}
then subsclibe to onSort.
grid.onSort.subscribe(function (e, args) {
grid.invalidateAllRows();
grid.render();
});
then next,,,,
I guess I should put sortfn somewhere though, but how??
where should I put sortfn??
Check out the examples here. There is no default sorting in the grid - this is left to the datasource to manage.
This example uses the native javascript sort property of the source data array to sort the rows:
grid = new Slick.Grid("#myGrid", data, columns, options);
grid.onSort.subscribe(function (e, args) {
var cols = args.sortCols;
data.sort(function (dataRow1, dataRow2) {
for (var i = 0, l = cols.length; i < l; i++) {
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field], value2 = dataRow2[field];
var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result != 0) {
return result;
}
}
return 0;
});
grid.invalidate();
grid.render();
});
This example outsources the sorting to the DataView object which is the grid's datasource.
grid.onSort.subscribe(function (e, args) {
sortdir = args.sortAsc ? 1 : -1;
sortcol = args.sortCol.field;
if (isIEPreVer9()) {
// using temporary Object.prototype.toString override
// more limited and does lexicographic sort only by default, but can be much faster
var percentCompleteValueFn = function () {
var val = this["percentComplete"];
if (val < 10) {
return "00" + val;
} else if (val < 100) {
return "0" + val;
} else {
return val;
}
};
// use numeric sort of % and lexicographic for everything else
dataView.fastSort((sortcol == "percentComplete") ? percentCompleteValueFn : sortcol, args.sortAsc);
} else {
// using native sort with comparer
// preferred method but can be very slow in IE with huge datasets
dataView.sort(comparer, args.sortAsc);
}
});
I need to sort data consisting of numbers and letters, this has been working very well for me.
function comparer(a, b) {
var collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: "base",
});
var x = a[sortcol];
var y = b[sortcol];
return collator.compare(x, y);
}
// add event listener to sort the grid
grid.onSort.subscribe(function (e, args) {
dataView.sort(comparer, args.sortAsc);
});
I know there's been a lot of questions like these around the site, but I'm in a struggle and I have tried to do my homework before asking.
I have an array of objects that each have three fields. Status, Type, and Time. All are integers.
Status is between 1-9 and represents an availability and everything
is sorted by status.
Type represents if the user is 0 - "Paid" or 1 -"Free". And paid are
always above free.
this is my code for that
function sortListings(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
if (typeA == typeB) {
return (statusA < statusB) ? -1 : (statusA > statusB) ? 1 : 0;
} else {
return (typeA < typeB ) ? -1 : 1;
}
}
And this works great. Now sometimes two objects will have the same status and be in the same pay type. So I'd like, in this case, to present the latest time stamp first.
Time is stored as an int ( unix )
I don't know how to go about this. Here is my attempt :
function sortListing(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
var timeA = obj1.time;
var timeB = obj2.time;
if (typeA == typeB) { // has the same type
if (statusA == statusB) { // has the same status
return timeA - timeB; //so sort by time
} else { // different statues, same type
return (statusA < statusB) ? -1 : (statusA > statusB) ? 1 : 0; // sort by status
}
} else {
return (typeA < typeB ) ? -1 : 1;
}
}
As you can see my knowledge of the inner workings of sort is not that great.
Any articles, answers or comments are greatly appreciated.
Your main issue is fall-through to less significant fields if the higher level fields are identical. Try this:
function sortListing(obj1, obj2) {
function compareType(a, b) {
return a.type - b.type;
}
function compareStatus(a, b) {
return a.status - b.status;
}
function compareTime(a, b) {
return a.time - b.time;
}
return compareType(obj1, obj2) ||
compareStatus(obj1, obj2) ||
-compareTime(obj1, obj2); // negative to reverse order
}
The || short circuit operator will cause the second (and subsequently third) comparison to be evaluated only if the prior comparison returns 0. The sort order is trivially changed just by changing the order in which the three functions are called.
The inner functions could, of course, be exposed in a higher level scope, allowing you to use each of those comparator functions individually, or in alternate orders.
Note also how this method avoids dereferencing any of the object properties unless absolutely necessary. If you were sorting thousands of entries that can make a significant difference, although in practise that might be offset by the potential expense of making three function calls internally.... Only benchmarks can really tell.
I would structure the code differently. Compare the most significant key first, then the next most significant, and so on. Only when you've compared all keys and found them all to be equal do you return 0.
function sortListing(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
var timeA = obj1.time;
var timeB = obj2.time;
if (typeA < typeB)
return -1;
if (typeA > typeB)
return 1;
if (statusA < statusB)
return -1;
if (statusA > statusB)
return 1;
if (timeA < timeB)
return -1;
if (timeA > timeB)
return 1;
return 0;
}
Now, any time you see a piece of code that looks like the same thing repeated over and over, a light should go off in your head that something can be generalized:
function compareKeys(k1, k2) {
for (var i = 0; i < k1.length; ++i) {
if (k1[i] < k2[i]) return -1;
if (k1[i] > k2[i]) return 1;
}
return 0;
}
function sortListing(obj1, obj2) {
return compareKeys([obj1.type, obj1.status, obj1.time], [obj2.type, obj2.status, obj2.time]);
}
Another refinement:
function pluck(obj, keynames) {
var keys = [];
for (var i = 0; i < keynames.length; ++i) // could be done with .map()
keys.push(obj[keynames[i]]);
return keys;
}
function sortListing(obj1, obj2) {
var keynames = ["type", "status", "time"];
return compareKeys(pluck(obj1, keynames), pluck(obj2, keynames));
}
I couldn't resist trying out a solution that emphasizes the recursive nature of this problem. You're basically comparing two arrays like this: you compare the first elements and if the first elements are the same then you compare the rest in the same way.
Here is the function to compare the arrays (it's assuming that arrays are the same length):
function compareArray(a1,a2) {
if (a1[0]==a2[0]) {
if (a1.length==1) {
return 0;
}
else {
return compareArray(a1.slice(1),a2.slice(1));
}
}
else {
return (a1[0]<a2[0] ) ? -1 : 1;
}
}
Same function with try/catch to check length:
function compareArray(a1, a2) {
var l = a1.length;
try {
if (l != a2.length) throw "arrays have diff. size";
if (l == 0) throw "empty array";
var h1 = a1[0];
var h2 = a2[0];
if (h1 == h2) {
if (l == 1) {
return 0;
} else {
return compareArray(a1.slice(1), a2.slice(1));
}
} else {
return (h1 < h2) ? -1 : 1;
}
} catch (err) {
// handle err
}
}
Then you can compare the fields
function sortListings(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
var timeA = obj1.time;
var timeB = obj2.time;
return compareArray([statusA,typeA,timeA],[statusB,typeB,timeB])
}
Btw you can use the compareArray to compare any number of fields.
Is there obvious reason why this Slickgrid example shouldn't work. Basically it doesn't sort on clicking columns.
var grid;
var columns = [
{id:"title", name:"Title", field:"title", sortable: true},
{id:"duration", name:"Duration", field:"duration", sortable: true},
{id:"%", name:"% Complete", field:"percentComplete", sortable: true},
{id:"start", name:"Start", field:"start", sortable: true},
{id:"finish", name:"Finish", field:"finish", sortable: true},
{id:"effort-driven", name:"Effort Driven", field:"effortDriven", sortable: true}
];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
$(function() {
var data = [];
for (var i = 0; i < 500; i++) {
data[i] = {
id: i,
title: "Task " + i,
duration: "5 days",
percentComplete: Math.round(Math.random() * 100),
start: "01/01/2009",
finish: "01/05/2009",
effortDriven: (i % 5 == 0)
};
}
var dataView = new Slick.Data.DataView();
grid = new Slick.Grid("#myGrid", dataView, columns, options);
function comparer(a,b) {
var x = a[sortcol], y = b[sortcol];
return (x == y ? 0 : (x > y ? 1 : -1));
}
var sortcol = "json_number";
var sortdir = 1;
grid.onSort.subscribe(function(e, args) {
sortdir = args.sortAsc ? 1 : -1;
sortcol = args.sortCol.field;
// using native sort with comparer
// preferred method but can be very slow in IE with huge datasets
dataView.sort(comparer, args.sortAsc);
});
dataView.beginUpdate();
dataView.setItems(data);
dataView.endUpdate();
grid.invalidate();
grid.render();
$("#myGrid").show();
})
Try adding this listener, which re-renders the grid when rows get shuffled around:
dataView.onRowsChanged.subscribe(function(e,args) {
grid.invalidateRows(args.rows);
grid.render();
});
Original example here: http://mleibman.github.com/SlickGrid/examples/example4-model.html
Even though there already is an accepted answer which surely solved the initial question, I wanted to point out a common mistake on stackoverflow while handling Slickgrid's subscribe method.
Let us imagine our grid is in the variable called 'grid', like in most other examples.
This occurs in most of the accepted and/or upvoted answers:
dataView.onRowsChanged.subscribe(function(e,args) {
grid.invalidateRows(args.rows);
grid.render();
});
or
grid.onSort.subscribe(function(e, args){
var cols = args.sortCols;
data.sort(function(dataRow1, dataRow2){
for (var i = 0, l = cols.length; i < l; i++){
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field], value2 = dataRow2[field];
var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result != 0) return result
}
return 0;
})
grid.invalidate()
grid.render()
})
Do this examples work as intended? Yes, they do.. under certain circumstances.
Let us imagine a function which adds a Slickgrid to a list of Slickgrids:
var m_grids = []
function anyName(){
var grid;
//..run code
//..run subscribe
m_grids.push(grid)
}
So what happends now when we are trying to call any subscribe function, while the subscribe function contains the variable grid? It merely affects the last assigned grid, no matter on which one the subscribe got executed on.
The correct way to subscribe those functions is by the args parameter:
dataView.onRowsChanged.subscribe(function(e,args) {
args.grid.invalidateRows(args.rows);
args.grid.render();
});
or
grid.onSort.subscribe(function(e, args){
var cols = args.sortCols;
args.grid.getData().sort(function(dataRow1, dataRow2){
for (var i = 0, l = cols.length; i < l; i++){
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field], value2 = dataRow2[field];
var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result != 0) return result
}
return 0;
})
args.grid.invalidate()
args.grid.render()
})
This might be a minor case since it requires usually more than one Slickgrid on the same page, yet why make it wrong when we could do it correct very easily :)
I have an array of objects gAllMedicalFilesClaimantsArray with 2 properties (UserID & UserInfo)
For example:
gAllMedicalFilesClaimantsArray[0].UserID = "111";
gAllMedicalFilesClaimantsArray[0].UserInfo = "AAA-111";
gAllMedicalFilesClaimantsArray[1].UserID = "222";
gAllMedicalFilesClaimantsArray[1].UserInfo = "BDD-478333";
What is the fastest way to check whether a specific UserID exists in the array using Jquery or Javascript because gAllMedicalFilesClaimantsArray has got 8000 records?
Thanks
var match = '222';
var matches = $.grep(myArray, function(el, index) {
return (el.UserID === match);
});
You can fasten the search process by using Binary Search algorithm if the array is sorted (e.g with respect to UserId).
function binarySearch(array, userid) {
var low = 0, high = array.length - 1,
i, comparison;
while (low <= high) {
i = parseInt((low + high) / 2, 10);
if (array[i].UserId < userid) { low = i + 1; continue; };
if (array[i].UserId > userid) { high = i - 1; continue; };
return array[i];
}
return null;
};
You can find the user of which ID is 12 by using the function:
var result = binarySearch(gAllMedicalFilesClaimantsArray, 12);
Something like this, I believe:
function exists(uid) {
var k = gAllMedicalFilesClaimantsArray.length;
uid = uid.toString(); // ensure the arg is a str (this can be omitted)
while (k--) {
if (gAllMedicalFilesClaimantsArray[k].UserID === uid) {
return true;
}
}
return false;
}
Is the array sorted by the UserID? If so, it can be improved either further by using a binary search; that would change this from O(n) to O(log n). Your example suggests it is. I found a good implementation of a binary search in JavaScript on the web, here. Here is the code if the site ever dies:
function binarySearch(items, value){
var startIndex = 0,
stopIndex = items.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
while(items[middle] != value && startIndex < stopIndex){
//adjust search area
if (value < items[middle]){
stopIndex = middle - 1;
} else if (value > items[middle]){
startIndex = middle + 1;
}
//recalculate middle
middle = Math.floor((stopIndex + startIndex)/2);
}
//make sure it's the right value
return (items[middle] != value) ? -1 : middle;
}
ExistsInArray(value, array){
for(var item in array){
if(item.UserId == value){
return true;
}
}
return false;
}
You can either prototype Array object, like this:
Array.prototype.exists = function(value, prop){
var i = null;
for (i in this)
if (this[i][prop] && this[i][prop] == value)
return true;
return false;
}
gAllMedicalFilesClaimantsArray.exists('222', 'UserID');