I am using this jQuery plugin to sort elements:
http://james.padolsey.com/javascript/sorting-elements-with-jquery/
It works well with this code:
function sortGallery(element, sorting) {
$('input.sort').removeClass('active');
$(element).addClass('active');
if (sorting === 'bydate') {
$('#gallery-js > div').sortElements(function(a, b){
return $(a).find('img').attr('data-date') < $(b).find('img').attr('data-date') ? 1 : -1;
});
} else if (sorting === 'random') {
console.log('TODO');
}
}
The problem is I do not understand the return statement. Can someone tell me how the return value for the "random" part should look like and if possible a short description how it works?
Alright, first you might want to read up on how Array#sort works.
In the documentation, they provide this code as an example as to how the sort order works
function compare(a, b) {
if (a is less than b by some ordering criterion)
return -1;
if (a is greater than b by the ordering criterion)
return 1;
// a must be equal to b
return 0;
}
Since you want to just order randomly, all we need to do is generate a random integer from -1 to 1
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
Then you simply call the method like this
} else if (sorting === 'random') {
return randomInt(-1, 1);
}
Related
I am stuck with a problem where I am supposed to figure out a mode from an array in typescript.
I am using the program Visual Studio Code and I know that I need a for loop but I am unsure what I should loop through it. I also have to make sure that if the array is empty, the number that always shows up is 0 and if there is two integers that show up the same amount of times, that the smaller number (whether positive or negative) is the number that is put as the mode.
Currently I have this part of the code:
export let mode = (a: number[]): number => {
let mode: number;
mode = a[0];
if (a.length === 0) {
return 0;
}
for (let i = 1; i < a. length; i++) {
if ()
return mode;
};
I know that there needs to be an if statement after the for loop that changes the mode when necessary, but I am unsure beyond that.
As you indicated: your mode should be a function which:
accepts an array of numbers and should return the number with highest occurrence.
if there are two or more numbers that share the highest occurrence, then it should return the number with the least value.
if the given array is empty, it should return 0.
You could do this:
If given array is empty, return 0 right away. Else proceed with the rest of the steps.
Count the occurrences of each number in the array.
Sort the result of #2 by number of occurrence and value. Sort by highest occurrence, then lowest value.
Get the first of the items from #3.
Here's an implementation using reduce() to do step #2, and sort() to do step #3.
let mode = (numbers: number[]): number => {
if (numbers.length === 0) {
return 0;
}
const m = numbers.reduce((items, current) => {
const item = (items.length === 0) ? null : items.find((x) => x.value === current);
(item) ? item.occurrence++ : items.push({ value: current, occurrence: 1 });
return items;
}, [])
.sort((a, b) => {
if (a.occurrence < b.occurrence) {
return 1;
} else if (a.occurrence > b.occurrence || a.value < b.value) {
return -1;
} else {
return (a.value === b.value) ? 0 : 1;
}
});
return m[0].value;
}
i used Math.abs but I fail and found this solution, I am following on eloquent javascript and how did negative value is turned to positive, here is the code:
function isEven(n) {
if (n == 0) {
return true;
}
else if (n == 1) {
return false;
}
else if (n < 0) {
console.log(-n);
return isEven(-n); // turn -3 to 3?
}
else {
return isEven(n - 2);
}
}
console.log(isEven(-3));
Here you are a simpler test case:
> console.log( -(-3) );
3
This is not a JavaScript peculiarity, it's how maths work.
Console has nothing to do with this.
Think back to your math class. -n is a shortened expression for (-1) * n. If you multiply two negative numbers, the result is a positive number - and since you're multiplying by negative 1 (where positive 1 is identity for multiplication), the result is the same number, but positive.
Since you're checking if (n < 0) before you multiply by -1, you'll always get a positive number.
However, this is almost definitely not what you want - the code you found seems to be an example of how to use recursion to solve common problems. In real-world Javascript, you'd want something more like this:
function isEven(x)
{
return (Math.abs(x) % 2) === 0;
}
it has to be recursion
Lets break the mold! Using -1 instead of 2. Assuming an integer;
function isEven(x) {
if (x === 0) return 1; // ended
return -1 * (isEven(Math.abs(x) - 1) ? 1 : -1) === 1;
}
Other fun ways to test for even that don't need 2 or mod/remaineder
function isEven(x) {
return Math.round(Math.sin(5 * x / Math.PI)) === 0;
}
An O(log n) recursion
function isEven(x, a) {
if (!a) a = [true, false];
if (a.length > x) return a[x];
return isEven(x, a.concat(a));
}
I am using a jquery drop down table filter plug in for table filters:
https://github.com/rbayliss/Dropdown-Table-Filter
When I have a column in my table that is numerical however, the numbers are sorted as text, e.g. 1, 10, 100, 2, 20, 200 ...
in the code the sorter looks like:
if(options.sortOpt) {
opts.sort(options.sortOptCallback);
}
I think this is a recursive call to:
sortOptCallback: function(a, b) {
return a.text.toLowerCase() > b.text.toLowerCase();
},
how should I amend this so that it will sort numerical fields correctly? I have tried the following:
sortOptCallback: function (a, b) {
if (isNaN(parseInt(a)) || isNaN(parseInt(b))) {
return a.text.toLowerCase() > b.text.toLowerCase();
} else {
return a > b;
}
},
Thanks,
Your attempt is almost correct. The only problem is that you are still comparing the elements as strings after having determined that they are numeric. Furthermore, sort callbacks expect:
A positive number if a > b
A negative number if a < b
Zero if they are equal.
With this in mind, try this callback:
if( a == b) return 0;
var anum = parseInt(a,10);
var bnum = parseInt(b,10);
if( isNaN(anum) || isNaN(bnum)) {
return a.toLowerCase() > b.toLowerCase() ? 1 : -1;
}
return anum > bnum ? 1 : -1;
EDIT: #PaoloMoretti brought my attention to the fact that all your items are numerical. In that case, you can just do this:
return a-b;
Say I have a list of items, which are sorted using a given comparator. I would expect that after sorting into ascending order comparator(element[1], element[1+n]) should return -1 for all values of n> 1, because according to that comparator, element[1]
I am performing a custom sort and finding that after sorting there are instances where comparator(element[1], element[1+n]) returns 1. When I look at the instance I see that the comparator giving the correct output, i.e. element[1]>element[1+n]. I don't understand how this could be the case after performing a sort with that comparator.
If anyone has any idea of sort subtleties that I might have missed, I'd really appreciate their thoughts. Also, if I can provide more information that might shed light please let me know.
edit
I thought this might be a more general question, but in response to mplungjan have added the custom sorter below.
The sort is for a hierarchical dataset in the form of a flat list of objects. Each object has an id which might be as follows:
0 for root 1.
0-0 for its first child.
0-1 for its second child.
etc.
Each object in the list had a field 'parent' which has the id of its parent. Essentially data.sort isn't doing what I think it should be.
function CurrencyTreeSorter(a, b) {
a_id = a.id.split("-");
b_id = b.id.split("-");
if(a_id.length != b_id.length || a_id.slice(0, a_id.length-1).toString() != b_id.slice(0, b_id.length-1).toString()){
var i = 0;
while (i < a_id.length && i < b_id.length && a_id[i] == b_id[i]) {
i++;
}
if (i == a_id.length || i == b_id.length){
return a_id.length > b_id.length ? 1 : -1;
}
else{
while (a.level > i) {
a = getParent(dataMap, a);
}
while (b.level > i) {
b = getParent(dataMap, b);
}
}
}
var x, y;
if (a[sortcol] == "-") {
x = -1;
}
else {
x = parseFloat(a[sortcol].replace(/[,\$£€]/g, ""));
}
if (b[sortcol] == "-") {
y = -1;
}
else {
y = parseFloat(b[sortcol].replace(/[,\$£€]/g, ""));
}
return sortdir * (x == y ? 0 : (x > y ? 1 : -1));
}
This turned out to be an issue with Chrome, described here and here. Essentially it's not safe to return zero from the comparator/comparer.
So I have an array of arrays which contain only strings.
The array of arrays is to be displayed as a table and may have over 1000 rows with 20 or more values in each.
eg:
var arr = [
["bob","12","yes"],
["joe","","no"],
["tim","19","no"],
["dan","","yes"],
["tim","",""],
["dan","0",""]
]
the strings may contain anything that can be represented as a string, including: " ", "", "0" or "00-00-00" etc... and any column my be used for ordering.
I am sorting the arrays ascending and descending but some of the values I am sorting by are blank strings: "". How could I get the blank strings (only) to always be at the end of the new arrays in all modern browsers?
currently they are at the end when ascending but at the start when descending.
I am sorting like below (Yes I'm sure I can do it shorter too):
if (direction == "asc") {
SortedArr = arr.sort(function (a, b) {
if (a[colToSortBy] == '') {
return -1;
}
if (a[colToSortBy].toUpperCase() < b[colToSortBy].toUpperCase()) {
return -1;
}
if (a[colToSortBy].toUpperCase() > b[colToSortBy].toUpperCase()) {
return 1;
}
return 0;
});
} else {
SortedArr = arr.sort(function (a, b) {
if (a[colToSortBy] == '') {
return -1;
}
if (b[colToSortBy].toUpperCase() < a[colToSortBy].toUpperCase()) {
return -1;
}
if (b[colToSortBy].toUpperCase() > a[colToSortBy].toUpperCase()) {
return 1;
}
return 0;
});
}
Empty strings at the end
Working example on JSFiddle that puts empty strings always at the end no matter whether order is ascending or descending. This may be a usability issue, but this is the solution:
if (direction == "asc") {
SortedArr = arr.sort(function (a, b) {
return (a[col] || "|||").toUpperCase().localeCompare((b[col] || "|||").toUpperCase())
});
} else {
SortedArr = arr.sort(function (a, b) {
return (b[col] || "!!!").toUpperCase().localeCompare((a[col] || "!!!").toUpperCase())
});
}
I think your problem comes from the fact that you're checking if a[colToSortBy] is an emtpy string but you don't do it for b[colToSortBy].
I am writing an application that must work on IE, FF, Safari, Chrome, Opera, Desktop, Tablet (including iPad) & phones (including iPhone). This means I keep testing across browsers. I thus discovered that my sort procedure below wasn't working correctly in FF until I added the 'new section' part of the code. Reason being I did not take care of sorting when a numeric value is not supplied (dash, -). FF was also not working correctly with negative (-) values. This code now works perfectly across:
if (SortByID == 0) { //string values (Bank Name)
myValues.sort( function (a,b) {
var nameA = a[SortByID].toUpperCase(), nameB = b[SortByID].toUpperCase();
if (SortOrderID == 1) { //sort string ascending
if (nameA < nameB) { return -1; } else { if (nameA > nameB) { return 1; } }
} else { //sort string descending
if (nameA < nameB) { return 1; } else { if (nameA > nameB) { return -1; } }
}
return 0 //default return value (no sorting)
})
} else { //numeric values (Items)
myValues.sort(function (a, b) {
if (isNumber(a[SortByID]) && isNumber(b[SortByID])) { //
if (SortOrderID == 1) { //sort number ascending
return parseFloat(a[SortByID]) - parseFloat(b[SortByID]);
} else { //sort string descending
return parseFloat(b[SortByID]) - parseFloat(a[SortByID]);
}
} else { //one of the values is not numeric
//new section
if (!isNumber(a[SortByID])) {
if (SortOrderID == 1) { //sort number ascending
return -1;
} else { //sort number descending
return 1;
}
} else {
if (!isNumber(b[SortByID])) {
if (SortOrderID == 1) { //sort number ascending
return 1;
} else { //sort number descending
return -1;
}
}
}//New section
return 0;
}
})
}
I know it is long, but it is simple enough for me to understand. I hope it also addresses the browser issue raised by Chris J.
*isNumber is a simple function testing if value !isNAN
I used this way in my app...
You can tweak it to get the favorable result.
we also have the Number.MAX_SAFE_INTEGER
var arr = [10, "", 8, "", 89, 72]
var min = Number.MIN_SAFE_INTEGER
var sorted = arr.sort(function (a,b) {
return (a || min) - (b || min)
})
var cons = document.getElementById("console")
cons.innerText = "Ascending " + JSON.stringify(sorted) + "\n" + "Descending " + JSON.stringify(sorted.reverse())
<html>
<head></head>
<body>
<p id="console"></p>
</body>
</html>