Sorting JSON data with Knockout .sort() - javascript

I can sort my data using Knockout's .sort(). But, when I try to sort dynamically on a user's click, the sort goes haywire. Here's my code:
var patientReport = [{"first_name":"Lyle","last_name":"Erickson","patient_id":1000},{"first_name":"Janna","last_name":"Barr","patient_id":1001},{"first_name":"Shelly","last_name":"Delacruz","patient_id":1002},{"first_name":"Nissim","last_name":"Wong","patient_id":1003},{"first_name":"Yvonne","last_name":"Rocha","patient_id":1004},{"first_name":"Leo","last_name":"Holland","patient_id":1005},{"first_name":"Melinda","last_name":"Curtis","patient_id":1006},{"first_name":"Orlando","last_name":"Peters","patient_id":1007},{"first_name":"Miriam","last_name":"Bates","patient_id":1008},{"first_name":"Otto","last_name":"Hurley","patient_id":1009},{"first_name":"Doris","last_name":"Byrd","patient_id":1010}];
var myObservableArray = ko.observableArray(patientReport);
$('.sort_header').on('click', function() {
var data = $(this).data('header');
sortRows(data);
});
function sortRows(row) {
myObservableArray.sort(
function(left, right) {
return left.row == right.row ? 0 : (left.row < right.row ? -1 : 1);
}
);
}
Any ideas what is going wrong?

You need to use left[row] rather than left.row (and the same for right). The way you have it you are trying to sort by a property actually called "row". Using the square bracket syntax lets you use the property with the name specified by the variable row.
function sortRows(row) {
myObservableArray.sort(
function(left, right) {
return left[row] == right[row] ? 0 : (left[row] < right[row] ? -1 : 1);
}
);
}

Related

How to check if localStorage value is in array?

Based on this answer I should be using $.inArray, therefore I do:
var curPostId = $(".my_post_id").attr("data-id");
if($.inArray(curPostId, lines)) {
$('#'+localStorage.getItem('saveButton')).attr('disabled', true);
}
If I do: console.log(curPostId); I get 248 which is correct. Then if I do console.log(lines); I get [242, 248]
Lines is defined like this:
var lines = localStorage.getItem("lines") ? JSON.parse(localStorage.getItem("lines")) : [];
But the check if it's in Array doesn't happen as this it's not applied $('#'+localStorage.getItem('saveButton')).attr('disabled', true);
This is how I set daveButton on local storage
$(".save_post").on("click", function() {
if (counter >= 2) {
alert('nope');
return;
} else {
$(this).attr("disabled", "true");
localStorage.setItem('saveButton', $(this).attr('id'));
var thisId = $(".my_post_id").attr("data-id");
lines.push(thisId);
localStorage.setItem("lines", JSON.stringify(lines));
}
});
This question is a follow up to my previous question how to keep button state across different pages which has an answer that works but only partly.
Just use the Array.includes method instead - it's less confusing, more appropriately matches what you're looking for, and doesn't require jQuery.
if (lines.includes(curPostId)) {
// ...
Also note that you can simplify your syntax by assigning and getting from localStorage's dot properties directly, for example:
var lines = JSON.parse(localStorage.lines || '[]');
// ...
localStorage.saveButton = $(this).attr('id');
// ...
localStorage.lines = JSON.stringify(lines);
$.inArray return index of element so what happens is, your element is at '0' th index and if condition will be false if value is 0
So you can use
if($.inArray(curPostId, lines) !== -1) {
or Use includes method of ES6
if (lines.includes(curPostId)) {

How reverse sort on react

I'm trying to reverse this sort on click using ReactJS:
if (this.state.Prz==3) {
this.props.files.sort(function(a,b){
return a.lastModified < b.lastModified ? 1 : -1;
});
}
I tried many tricks searching on Google but I'm still stuck.
You can change the direction of sort (since you seem to be sorting by Date) by swapping 1 and -1:
this.props.files.sort(function(a,b){
return a.lastModified < b.lastModified ? -1 : 1;
});
Or you can reverse the array, assuming it was sorted before doing that (concat() is for making a copy of the array, keeping things "immutable"):
this.props.files.concat().reverse();
You can reverse an array simply by calling reverse
this.props.files.reverse()
Beware though that this will mutate the original array.
Here finally how I did. I add a condition.
if (this.state.activePrz==3) {
this.props.files.sort(function(a,b){
var asc = a.lastModified < b.lastModified;
var desc = a.lastModified > b.lastModified;
if(asc == true){
return asc;
}else{
return desc;
}
})
}
And it works.
I couldn't :
this.props.files.reverse();
or
this.props.files.concat().reverse();
The error message was :
Cannot read property 'files' of undefined
Many thanks for your help.

Is it possible to quicksort objects in an array based on a property? Javascript [duplicate]

To clarify a bit on the question, I have an array and within the array are a bunch of objects. Can I rearrange the objects in the array based on the key values of each object?
When I'm attempting to do so, it keeps telling me that the variable (in this case, students) is undefined.
When I use the built-in sort function, it works perfectly. However, this is a school assignment and I HAVE to show the breakdown of the quicksort function.
Here is the code I am using:
function swap(student, firstIndex, secondIndex){
var temp = student[firstIndex];
student[firstIndex] = student[secondIndex];
student[secondIndex] = temp;
}
function partition(student, left, right) {
var pivot = student[Math.floor((right + left) / 2)],
i = left,
j = right;
while (i <= j) {
while (student[i] < pivot) {
i++;
}
while (student[j] > pivot) {
j--;
}
if (i <= j) {
swap(student, i, j);
i++;
j--;
}
}
return i;
}
function quickSort(student, left, right) {
var index;
if (student.length > 1) {
left = typeof left != "number" ? 0 : left;
right = typeof right != "number" ? student.length - 1 : right;
index = partition(student, left, right);
if (left < index - 1) {
quickSort(student, left, index - 1);
}
if (index < right) {
quickSort(student, index, right);
}
}
return student;
}
var studentNumbersArray = []
var randomArray = [];
for (i = 0; i < studentsArray.length; i++) {
studentNumbersArray.push(studentsArray[i].studentNumber);
}
function sortStudentNumbers() {
var student = studentNumbersArray.name; // <-- THIS IS WHERE I AM GETTING ERROR
quickSort(student);
var updatedStudentArray = JSON.stringify(studentsArray);
localStorage.setItem("students", updatedStudentArray);
location.reload();
}
After seeing several permutations of your code I think what you are trying to do needs to look a little something like this.
quickSort(arrayToSort, attributeToSortOn, left, right) {
...
}
...
function partition(arrayToSort, attributeToSortOn, left, right) {
var pivot = student[Math.floor((right + left) / 2)][attributeToSortOn]
...
while (arrayToSort[i][attributeToSortOn] < pivot) {
...
}
...
quickSort(studentsArray, 'studentNumber');
quickSort always needs the array to compare values at each position. You can't just pass studentsArray.studentNumber because the attribute to sort on is useless on it's own and the array has no knowledge of the types of object contained within it anyway.
You cannot use a variable defined inside a function, in another function. Every function has its own scope, and in order to carry data between functions (inside the variables) you have to create a variable in the upper (global) scope. That means, you have to create the student and updatedStudentArray outside of the functions and use them without declaring in the functions (sortStudentNumbers() in this case)
var student;
var updatedStudentArray;
function swap(student, firstIndex, secondIndex){
var temp = student[firstIndex];
student[firstIndex] = student[secondIndex];
student[secondIndex] = temp;
}
// ..................................
// ------ removed for clearity
// ..................................
function sortStudentNumbers() {
student = studentNumbersArray.name; // <-- THIS IS WHERE I AM GETTING ERROR
quickSort(student);
updatedStudentArray = JSON.stringify(studentsArray);
localStorage.setItem("students", updatedStudentArray);
location.reload();
}
These the mistakes I found. But still, when I execute this code I get a studentsArray is not defined error on the console. Where is your studentsArray? If you have it somewhere, then it should all work now.
EDIT:
After you created the second question, I had a quick chance to have a look at your modified code. Don't forget to update your question here.
My Answer to your modified code:
You have to set a "student" key in localStorage first, in order to use getItem() for it. You don't have anything in the localStorage, that is why your variable is not filled when you try to get the data from there. Thus, you are getting the error "Cannot read property 'studentNumber' of null".

Filter a store with array of values from one property with ExtJS

I'm trying to apply a constraint on combobox. It's half-working at the moment.
On the combobox, I have this listener:
[...]
listeners: {
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var getOrgValue = orgComboVal.getValue();
if (typeof getOrgValue !== undefined) {
reseaulist.clearFilter(true);
for (var q = 0, l = getOrgValue.length; q < l; q++) {
reseaulist.filter([
{property:'code_organisme', value: getOrgValue[q]}
]);
}
}
}
}
Ext.getCmp('Org1') defines another combobox.
When orgComboVal.getValue() is a single value, the filter is well applying.
but when it's an array of value, eg ["5", "9"], it's not working and the combobox supposed to be filtered shows no value (so I guess a filter is still applied but in an incorrect way).
I guess it's because the reseaulist.filter is called multiple time.
How can I achieve this ?
I saw the filterBy method but I don't know how to make it work.
Also, this post is interesting : How to filter a store with multiple values at once? but same, can't make it work since
getOrgValue.split(',')
is showing an error
(Object Array has no method split)
Any tips ? I'm using ExtJS 4.2.
EDIT
Thanks to #rixo, I've made it.
Also, I had to change some of the code he provided me, because the value of the Org1 combobox was always an array, even if empty, so the store filter was never cleared.
Here it is :
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
console.log(values)
if (values != null) {
reseaulist.clearFilter(false);
if (Ext.isArray(values)) {
if (0 < values.length) {
reseaulist.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
reseaulist.clearFilter(true);
}
}
}
}
Each filter is applied one after the other on the previously filtered data set, so your code implements a logical AND. That's why all values are filtered out...
Here's an example using filterBy to accept any value that is in your array:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
store.clearFilter(false);
if (Ext.isArray(values)) {
store.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
record.get('code_organisme') === values;
}
} else {
store.clearFilter(true);
}
}
Or you could also use a regex with the filter method:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
var filterValue = Ext.isArray(values)
? new RegExp('^(?:' + Ext.Array.map(values, function(value){return Ext.escapeRe(value)}).join('|') + ')$')
: values;
store.clearFilter(false);
store.filter('code_organisme', filterValue);
} else {
store.clearFilter(true);
}
}
Concerning your error, arrays indeed don't have a split method. Strings can be split into an array. Arrays, on their side, can be joined into a string...
Try This....
var getOrgValue = "5,9,4"; // array of value
reseaulist.filterBy(function(rec, id) {
return getOrgValue.indexOf(rec.get('code_organisme')) !== -1;
});

Sorting Slickgrid by Multiple Columns?

I just started testing out Slickgrid for a project I'm working on and I'm very impressed with its performance. One requirement I have is sorting on multiple columns. I don't fully have my head wrapped around the Dataview in Slickgrid, so maybe I'm missing something obvious, but is there a way to sort a grid on multiple columns? Even if the UI can't handle sorting by more than one, I would like to be able to call a function with the columns in order, plus ascending or descending. I was able to do this with Datatables, but it doesn't have grouping (another requirement for the project).
In the worst case, I will resort to doing the sorting on the server and serving the content back to the client statically sorted.
I got it working for dataView with multi-column sort in the way. Was the easiest one to understand too. This is from the example in github, except that I've to pass one more parameter for dataView.sort(). It can always be true, and you can take care of the sort direction in your function.
grid.onSort.subscribe(function (e, args) {
gridSorter(args.sortCols, dataView);
});
function gridSorter(sortCols, dataview) {
dataview.sort(function (row1, row2) {
for (var i = 0, l = sortCols.length; i < l; i++) {
var field = sortCols[i].sortCol.field;
var sign = sortCols[i].sortAsc ? 1 : -1;
var x = row1[field], y = row2[field];
var result = (x < y ? -1 : (x > y ? 1 : 0)) * sign;
if (result != 0) {
return result;
}
}
return 0;
}, true);
}
Just in case it helps someone.
You can chain sort comparers to do multiple column sorting. Instead of doing
function comparerOnCol1(a, b) {
return a["col1"] - b["col1"];
}
function comparerOnCol2(a, b) {
return a["col2"] - b["col2"];
}
you can do
// sort by col1, then col2
function combinedComparer(a, b) {
return comparerOnCol1(a, b) || comparerOnCol2(a, b); // etc.
}
or just implement it inline.
As far as reflecting the sort order in the UI, while you can't do directly, you can apply the sort indicators by setting "headerCssClass" on the column definitions you're sorting by and having them display the arrows (or however else you're indicating sort columns).
There's an example here that uses the 'multiColumnSort' option.
http://mleibman.github.com/SlickGrid/examples/example-multi-column-sort.html
I don't think it works though, because args.sortCols is always an array of 1.
[Edit]
In order for it work, I need to hold shift before clicking on a column header (not very intuitive IMHO)
See also: https://github.com/mleibman/SlickGrid/pull/276
I spent a while trying to solve this with dataview (without shift key shenanigans) and I think I found the way to do it.
Use single column sort {multiColumnSort: false} and store the sort arguments in a closure. Defer to the previous comparitor if fields are equal.
var currentSortCmp = null;
grid.onSort.subscribe(function (e, args) {
// declarations for closure
var field = args.sortCol.field;
var sign = args.sortAsc ? 1 : -1;
var prevSortCmp = currentSortCmp;
// store closure in global
currentSortCmp = function (dataRow1, dataRow2) {
var value1 = dataRow1[field], value2 = dataRow2[field];
//if equal then sort in previous closure (recurs)
if (value1 == value2 && prevSortCmp)
return prevSortCmp(dataRow1, dataRow2);
return (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
};
dataView.sort(currentSortCmp);
grid.invalidate();
grid.render();
});
remembers all previous orders. just works. works as expected.

Categories

Resources