I have an array that at page load sorts the array how I want, depending on the current date it shows the objects that have a future date and then shows the objects with past dates, lets call this datesToPlay.
I have two radio buttons to call on the method to sort the same array, and at page load it sorts the array perfectly fine how I mentioned above. The problem is when I sort the array with the other sorting method, createdAt which simply sorts the array by the date of creation. This method sorts it fine, but when I press the other radio to sort by datesToPlay it doesn't sorts the array.
function SortByPlayTime(a, b){
var currentDate = new Date();
if(lastDateOfObjectsInArray > currentDate){
if(a.attributes.start >= currentDate){
if(a.attributes.start > b.attributes.start && b.attributes.start >= currentDate)
return 1;
else
return -1;
} else if(b.attributes.start >= currentDate){
if(b.attributes.start > a.attributes.start)
return -1;
else
return 1;
}
else{
if(a.attributes.start > b.attributes.start)
return 1;
else
return -1;
}
} else{
if(a.attributes.start > b.attributes.start)
return -1;
else
return 1;
}
function SortByCreation(a, b){
if(a.attributes.createdAt > b.attributes.createdAt)
return 1;
else
return -1;
Basically what Im doing is I have an array with all the objects im trying to sort, this array varies in size and can be like 1000 or more objects.
In function loadArrayToShowFilters() what Im doing is to prepare a new array that will be shown in a table (see comments above for screens). This is cause Im mimicking a table but actually Im doing all the work with arrays; this array will always be 100 length or less.
function loadArrayToShowFilters() {
//array = the array of objects Im trying to filter
var sizeArrayToShow;
if(array.length < 100)
sizeArrayToShow = array.length;
else
sizeArrayToShow = 100;
arrayTableToShow = [];
//arrayTableToShow = This is the array that holds the objects that are shown on the table
for (var i = 0; i < sizeArrayToShow; i++) {
arrayTableToShow[i] = array[i];
};
Events that trigger the sorts: event click on two radio buttons.
Code that performs the actual sort: on the event click of each radio button, I just do array.sort(SortByCreation) and like so respectively.
Sample data: {"start":{"__type":"Date","iso":"2018-02-01T11:00:00.000Z"},"end":{"__type":"Date","iso":"2018-02-01T12:00:00.000Z"},"createdAt":"2018-01-29T20:37:51.477Z","updatedAt":"2018-02-23T03:12:15.968Z","objectId":"dSVZXFAIyf"}
It's basically just an array with objects, each object with a variable attributes with includes variables start and createdAt which are used to do the sorting
Events:
'click #lastCreated'(event){
orderArregloCreatedAt();
},
'click #nextToPlay'(event){
orderArregloPlayTime();
}
function orderArregloCreatedAt() {
array.sort(SortByCreation);
loadArrayToShowFilters();
}
function orderArregloPlayTime() {
array.sort(SortByPlayTime);
loadArrayToShowFilters();
}
Information needed to resolve this issue is still sorely lacking from this question. But I think the fault lies in the sorting algorithm of SortByPlayTime.
This is a bit of a wild shot, but please can you tell me if the following change delivers:
expected sort order at page load?
expected sort order after clicking the radio button?
the same results for both, even if it's wrong?
Code change:
function SortByPlayTime(a, b) {
var currentDate = new Date();
// a is future, b is past, so a shows above b = -1
if (a.attributes.start >= currentDate && b.attributes.start < currentDate) return -1;
// a is past, b is future, so a shows below b = 1
if (a.attributes.start < currentDate && b.attributes.start >= currentDate) return 1;
// both a and b are similarly future or past so compare on their values alone, in ascending order
// if a > b then it should show below b, = positive result
return a.attributes.start - b.attributes.start;
}
Related
i am trying to create google-form which is used to register students agreements on practice. Every agreement is registered and got agreement number which format is Last to digits of current year-T-number of agreement at this year/M. For example for now it is 17-T-11/M. The number of agreement currently is written by person which is responsible for practice.
Here is code of script below:
function onChange(e)
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var range = sheet.getDataRange();
var values = range.getValues();
var comboValues = ['16-T-105/M'];
// removing titles from 0 column and 1 line (titles)
for (var i = 1; i <= values.length; i++) {
var v = values[i] && values[i][0];
v && comboValues.push(v)
}
// Sort the values
comboValues.sort(
function(a, b) {
if (b.toLowerCase() < a.toLowerCase()) return -1;
if (b.toLowerCase() > a.toLowerCase()) return 1;
return 0;
}
);
Logger.log(comboValues);
// google-form modification
var form = FormApp.openById('1SHgVIosoE34m9cny9EQySljvgnRpzffdFEZe-kzNOzA');
var items = form.getItems();
for (i = 4; i < items.length; i++) {
Logger.log("ID: " + items[i].getId(), ': ' + items[i].getType());
}
form.getItemById('2087613006').asListItem().setChoiceValues(comboValues);
I got issue which is related with lexicographical order. Person which register agreement choose from list last registered agreement number: i tryed to do that last registered agreement number will always be at list top. As time when i started this everything was fine (it started with number 16-T-105/M), but new year come and soon after 17-T-10/M agreement was registered i got issue, that 17-T-10/M was not on list top. Soon i realised that this happens because script use lexicographical order and "thinks" that 2 is more than 10. So i understood that i somehow will have to change that order and do that 2 is less than 10, 11 is less than 101 and so on.
My question is how to do that? I guess that i need to sort array elements in natural order - but i do not have idea how to do this.
I tryed to google how to do it , but result was not satisfactory - maybe my knowledge of coding is pretty limited (i am PhD student of Psychology, not Informatics) :)
Maybe someone will help how to solve that problem.
Updates:
Link to spreadsheet: https://docs.google.com/spreadsheets/d/1FH5qYTrLUNI2SCrcaqlwgu8lzAylaTkZsiALg0zIpCM/edit#gid=1620956794
Link to google-form (Copy of actual form): https://docs.google.com/forms/d/e/1FAIpQLSerJfkv1dgHexUwxppXNyhb46twOZgvEMOIVXSOJoED3SLmyQ/viewform
You should adjust the sorting method to account of the peculiarities of the data. Here is one way to do this: the function splitConvert processes each string, splitting it by non-word characters and then converting what can be converted to integers (and lowercasing the rest). Then the comparison goes through this array one by one.
comboValues.sort(
function(a, b) {
var as = splitConvert(a);
var bs = splitConvert(b);
for (var i = 0; i < as.length; i++) {
if (bs[i] < as[i]) return -1;
if (bs[i] > as[i]) return 1;
}
return 0;
}
);
function splitConvert(str) {
return str.split(/\W/).map(function(part) {
var x = parseInt(part, 10);
return isNaN(x) ? part.toLowerCase() : x;
});
}
This is not the most performance-oriented solution: the split-parse function will be repeatedly called on the same strings as they are being sorted. If this becomes an issue (I don't really think so), one can optimize by having one run of conversion, creating an array of arrays, and then sorting that.
I might have close to 40 elements in the array. Ocasionally the array is sorted as I want it to be sorted but after I refresh the webpage, the order of sorting completely changes even if the information itself remains the same and the process of sorting is the same.
I notice that I get "2000 messages have not been logged" in the console, so I assume the logger gets overloaded.
Therefore, my first question is: do javascript executions have any 'timeout' as in a php script, for example?
And if not, what could be wrong with my code?
arr.sort(function (a, b) {
var a_freq = a["first"],
b_freq = b["first"],
a_imp = a["second"],
b_imp = b["second"],
a_ldone = a["third"],
b_ldone = b["third"];
var imp_diff = (a_imp - b_imp) / 10;
var relation;
if (imp_diff == 0){
if(b_ldone > a_ldone){
return 1;
}else if(b_ldone == a_ldone){
if(a_freq >= b_freq){
return 1;
}else{
return -1;
}
}else{
return -1;
}
}else if(imp_diff > 0){
if(b_ldone > imp_diff && a_ldone - b_ldone > imp_diff){
return 1;
}
return -1;
}else{
imp_diff = imp_diff * (-1);
if(a_ldone > imp_diff && b_ldone - a_ldone > imp_diff){
return -1;
}
return 1;
}
}
});
When I return 1, am I advancing element 'a' or 'b'?
As I said, the values stay the same with every refresh of the webpage but for some reason the order changes... Within every refresh, though, the value of the final array is saved in the cache, so the new array that is being sorted in a refresh is an array that has been sorted previously, but the values of each objet in the array stay the same... Any reason for it to change, sometimes so abruptly? Does the javascript process simply halt without completely finishing sorting the whole array? What would be the reason for this? As I wrote, I must have around 40/50 elements in the array... Is this too much to handle? Is there any other possible motif?
In D3 I am working on customizing and adding to this timeline: http://bl.ocks.org/rengel-de/5603464 by rengel Rengel. All has been going well until when recently I have been having a problem showing all of the events on my timeline from my JSON dataset. Instead of showing from 1970-1953, it shows only 1966-1953.
D3 Only Showing Partial Results from JSON
My dates in my JSON file are in YYYY-MM-DD format, and I am laying out the timeline using tracks. This function appends elements into these tracks and it sorts through the results from later to earlier dates (see code below). But for some reason, if I sort these JSON results backwards, my dates only start at 1966, and if I sort forward() they only start half way through (1958) from the opposite side. I have been thinking this is happening because the dates aren't being calculated properly.
Here is the compareDescending function that checks each pair of dates:
function compareDescending(item1, item2) {
// Every item must have two fields: 'start' and 'end'.
// Read the dataset in and then compare the dates of the first value with the next value
var firstdatestartdesc = new Date(item1.DateStart);
var seconddatestartdesc = new Date(item2.DateStart);
// later first
var result = new Date(item1.DateStart) - new Date(item2.DateStart) ;
if (result < 0) { return 1; }
if (result > 0) { return -1; }
return 0;
var oneDay = 24*60*60*1000; // hours*minutes*seconds*milliseconds
var firstdatefinishdesc = new Date(item1.DateFinish);
var seconddatefinishdesc =new Date(item2.DateFinish);
result = new Date(item2.DateFinish) - new Date(item1.DateFinish);
if (result < 0) { return 1; }
if (result > 0) { return -1; }
return 0;
}
I am then using the calculateTracks function to sort through:
function calculateTracks(item, sortOrder, timeOrder) {
var i, track;
sortOrder = sortOrder || "descending"; // "ascending", "descending"
timeOrder = timeOrder || "backward"; // "forward", "backward"
function sortBackward() {
// older items end deeper
data.projects.forEach(function (item) { /
for (i = 0, track = 0; i < tracks.length; i++, track++) {
if (item.DateFinish < tracks[i]) { break; }
}
item.track = track
tracks[track] = item.DateStart;
});
}
//won't need to use this when we have sort backward instead
function sortForward() {
// younger items end deeper
data.projects.forEach(function (item) {
for (i = 0, track = 0; i < tracks.length; i++, track++) {
if (item.DateStart > tracks[i]) { break; }
}
item.track = track;
tracks[track] = item.DateFinish;
});
}
if (sortOrder === "ascending")
data.projects.sort(compareAscending);
else
data.projects.sort(compareDescending);
if (timeOrder === "forward"){
;
sortForward();
}else{
sortBackward();
}
}
While debugging this, I can see that changing the value of the "result" variable produces different layouts, so that's why I think that it isn't computing dates properly. So I have then tried a number of other ways to compare dates, but no luck yet. (Actually in one case, the rectangles displayed from 1970 back to 1953, but instead of 'stacking' in lanes, each rectangle was added below the preceding rectangle, but the rectangles ended up below the x-axis without stacking properly. The following adjustments were tried with the result variable:
var oneDay = 24*60*60*1000; // hours*minutes*seconds*milliseconds
var result = Math.round(firstdatestartdesc.getTime() - seconddatestartdesc.getTime());
var result = Math.round(Math.round(firstdatestartdesc - seconddatestartdesc)/(oneDay));
var result = Math.round((firstdatestartdesc - seconddatestartdesc)/(oneDay));
var result = Math.round((firstdatestartdesc - seconddatestartdesc)/(oneDay));
How might I get all of the rectangles to display, instead of the results starting only half way through the dataset?
Thanks in advance.
Nothing to do with dates, as thanks to the power of that fully armed and operational jsfiddle, your problem was this:
vis.selectAll ("g:not(.xaxis)")
should be
vis.selectAll ("g.rectclass")
g:not(.xaxis) only excludes the xaxis, but not the child g elements that hold the axis labels. So your first X data points are getting joined to these g elements that hold these labels. Since they already exist, they don't get picked up by the .enter() clause and your first X results are discarded (which is also why your missing results changed depending on which end you started sorting from.)
I am trying to sort a dynamically constructed table on the client side. So far I have done my research to discover JavaScript's sort() method will take a callback. Here is what I have so far:
function retrvCatalog(e){
var merch = document.getElementById('merch');
var tRows = merch.rows;
var tBody = merch.tBodies;
var rowArr = [];
for (x in tRows){
rowArr[x] = tRows[x];
}
rowArr.sort(function(a, b){
if (a.cells.textContent < b.cells.textContent){
return -1;
}
if(a.cells.textContent > b.cells.textContent){
return 1;
}
return 0;
});
}
Stepping through it in Firebug, it appears to not change the order of the rows. Can someone please help me figure out what I am missing?
FINAL ALGORITHM
function retrvCatalog(e){
var fltr = e.id;
var merch = document.getElementById('merch');
var tblHead = merch.tHead;
merch.deleteTHead();
var tRows = merch.rows;
var rowArr = [];
for (var i=0; i<tRows.length; i++){
rowArr[i] = tRows[i];
}
rowArr = rowArr.sort(function(a, b){
if (fltr > 3){
a = parseFloat(a.cells[fltr].innerHTML);
b = parseFloat(b.cells[fltr].innerHTML);
}
else{
a = a.cells[fltr].innerHTML;
b = b.cells[fltr].innerHTML;
}
if (a>b){
return 1;
}
if(a<b){
return -1;
}
return 0;
});
while(merch.hasChildNodes()) {
merch.removeChild(merch.firstChild);
}
merch.appendChild(tblHead);
for (i=0;i<rowArr.length;i++){
merch.appendChild(rowArr[i]);
}
}
The final two columns in the row are numbers, so that is why the method to sort is slightly variable.
Several problems in your code.
First, you didn't declare the x variable.
for(var x...
Second, don't use for-in to iterate an array like collection. Use for.
for (var x = 0, len = tRows.length; x < len; x++){
rowArr[x] = tRows[x];
}
Third, there is no textContent property of a cells collection.
This is easy to test by logging its value. This should have been the first thing you tried.
console.log(a.cells.textContent); // undefined
You need to decide which cell you want, and ask for it by index.
console.log(a.cells[0].textContent);
Finally, you should be aware that this technique will not show the result of the sorting in the DOM. You're only sorting the Array. You'll need to append the new ordering to the DOM.
Maybe you knew this, but you didn't show it in your code.
I don't know the relationship of the rows to the tBodies, so I can't give an example. But if all the rows are in one tbody, just loop the Array, and tBody[0].appendChild(rowArr[i])
I'm not sure if I'm missing something, but I'm pretty sure you can't use textContent on the cells array. You need to index cells so you know which column to actually sort on. If your rows have 4 columns each (or even if there's only 1), you still need to tell the sort function which column to sort on.
So in your sort function, if you wanted to sort by the second column, you'd want something like:
rowArr.sort(function (a, b) {
if (a.cells[1].textContent < b.cells[1].textContent) {
return -1;
} else if (a.cells[1].textContent > b.cells[1].textContent) {
return 1;
}
return 0;
});
And I'm not sure what's in your cells, but you may want to use .innerHTML, not .textContent.
rowArr.sort(function(a,b) {
a = parseFloat(a.cells.textContent);
b = parseFloat(b.cells.textContent);
return (a-b);
};
"don't use for-in to iterate an array like collection." - user1673729
tRows is not an array, it's an HTML collection. That is why I used "for in" – nodirtyrockstar
An HTML Collection is an array like collection. Do not use for-in.
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.