How to define default JavaScript sort callback function? - javascript

I'm attempting to create my own sort function (question Async version of sort function in JavaScript). I've taken merge sort function from Rosetta Code and make it async:
// based on: https://rosettacode.org/wiki/Sorting_algorithms/Merge_sort#JavaScript
async function mergeSort(fn, array) {
if (array.length <= 1) {
return array;
}
const mid = Math.floor(array.length / 2),
left = array.slice(0, mid), right = array.slice(mid);
await mergeSort(fn, left)
await mergeSort(fn, right)
let ia = 0, il = 0, ir = 0;
while (il < left.length && ir < right.length) {
array[ia++] = (await fn(left[il], right[ir]) <= 0) ? left[il++] : right[ir++];
}
while (il < left.length) {
array[ia++] = left[il++];
}
while (ir < right.length) {
array[ia++] = right[ir++];
}
return array;
}
But I'm not sure how I can define default function fn to work the same as in JavaScript.
console.log([1, 2, 3, 10, 11, 100, 20].sort());
What should be the default sorting function to match those in the JavaScript engine?
Should I convert numbers to strings and compare those? What is the proper implementation?

Updated Answer
The default sort method as defined in core.js looks like so
var getSortCompare = function (comparefn) {
return function (x, y) {
if (y === undefined) return -1;
if (x === undefined) return 1;
if (comparefn !== undefined) return +comparefn(x, y) || 0;
return toString(x) > toString(y) ? 1 : -1;
};
Taken from this repo: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.array.sort.js

Based on #jonrsharpe comments I was able to implement proper default function:
function defaultSortFn(a, b) {
if (typeof a !== 'string') {
a = String(a);
}
if (typeof b !== 'string') {
b = String(b);
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
that can be used in my sort:
Array.prototype.sort = function(fn = defaultSortFn) {
return mergeSort(fn, this);
};

The ECMAScript specification for Arra.prototype.sort mentions that the comparison (in absence of a comparator) involves:
e. Let xString be ? ToString(x).
f. Let yString be ? ToString(y).
g. Let xSmaller be ! IsLessThan(xString, yString, true).
h. If xSmaller is true, return -1𝔽.
i. Let ySmaller be ! IsLessThan(yString, xString, true).
j. If ySmaller is true, return 1𝔽.
k. Return +0𝔽.
Given that the IsLessThan procedure is also executed when two strings are compared with the < operator, we can faithfully replicate the default callback function as follows:
function (x, y) {
let xString = String(x);
let yString = String(y);
return xString < yString ? -1 : yString < xString ? 1 : 0;
}

Related

Convert Arrow function to function expression

I have a function with a map object:
function xml_encode(s)
{
return Array.from(s).map(c =>
{
var cp = c.codePointAt(0);
return ((cp > 127) ? '&#' + cp + ';' : c);
}).join('');
}
This has worked great except it has broken everything when running Internet Explorer 11.
I tried to rewrite the code using a function expression however I get a c is not defined:
function xml_encode(s)
{
return Array.from(s).map(function()
{
var cp = c.codePointAt(0);
return ((cp > 127) ? '&#' + cp + ';' : c);
}).join('');
}
Unfortunately this needs to be a public-facing function and I am required to support IE11 for now. How do I rewrite this function to work with IE11?
You're missing the argument of your function so try this
function xml_encode(s) {
return Array.from(s).map(function(c) {
var cp = c.codePointAt(0);
return ((cp > 127) ? '&#' + cp + ';' : c);
}).join('');
}
You are missing the c paramter to your anonymous function.
function xml_encode(s)
{
return Array.from(s).map(
function(c) {
//use charCodeAt for IE or use a polyfill
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt#Polyfill
var cp = c.codePointAt(0);
return ((cp > 127) ? '&#' + cp + ';' : c);
}
).join('');
}
Bonus:
You might want to use s.split('') instead of Array.from(s) for better performance and browser support. String.prototype.split is supported in every browser while Array.from is not supported in IE for example. split is also 30% faster on Chrome and 80% faster on FF on my PC.
Try to modify your code as below (you are missing an argument in the function):
function xml_encode(s) {
return Array.from(s).map(function (c) {
var cp = c.codePointAt(0);
return cp > 127 ? '&#' + cp + ';' : c;
}).join('');
}
Since, you are using Array.from and codePointAt function, they don't support IE Browser. To use them in the IE browser, we need to add the related popyfill before using this function.
Polyfill code as below (I have created a sample to test it, it works well on my side.):
// Production steps of ECMA-262, Edition 6, 22.1.2.1
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
// The length property of the from method is 1.
return function from(arrayLike/*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;
// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError('Array.from requires an array-like object - not null or undefined');
}
// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}
// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);
// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method
// of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
}());
}
/*! https://mths.be/codepointat v0.2.0 by #mathias */
if (!String.prototype.codePointAt) {
(function () {
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
var defineProperty = (function () {
// IE 8 only supports `Object.defineProperty` on DOM elements
try {
var object = {};
var $defineProperty = Object.defineProperty;
var result = $defineProperty(object, object, object) && $defineProperty;
} catch (error) { }
return result;
}());
var codePointAt = function (position) {
if (this == null) {
throw TypeError();
}
var string = String(this);
var size = string.length;
// `ToInteger`
var index = position ? Number(position) : 0;
if (index != index) { // better `isNaN`
index = 0;
}
// Account for out-of-bounds indices:
if (index < 0 || index >= size) {
return undefined;
}
// Get the first code unit
var first = string.charCodeAt(index);
var second;
if ( // check if it’s the start of a surrogate pair
first >= 0xD800 && first <= 0xDBFF && // high surrogate
size > index + 1 // there is a next code unit
) {
second = string.charCodeAt(index + 1);
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
}
}
return first;
};
if (defineProperty) {
defineProperty(String.prototype, 'codePointAt', {
'value': codePointAt,
'configurable': true,
'writable': true
});
} else {
String.prototype.codePointAt = codePointAt;
}
}());
}
More detail information, please check the Array.from() method and the codePointAt() method

Making compare() function more elegant

I have this code:
function compare (a, b) {
let comparison = 0;
if (a.essentialsPercentage < b.essentialsPercentage) {
comparison = 1;
} else if (a.essentialsPercentage > b.essentialsPercentage) {
comparison = -1;
} else {
if (a.skillsNicePercentage < b.skillsNicePercentage) {
comparison = 1;
} else if (a.skillsNicePercentage > b.skillsNicePercentage) {
comparison = -1;
} else {
if (a.startDate > b.startDate) {
comparison = 1
} else if (a.startDate < b.startDate) {
comparison = -1
}
}
}
return comparison;
}
What would be the most elegant way of writing it? It doesn't seems nice at the moment.
Assuming this is being used as the comparison function for Array.prototype.sort(), only the sign of the result matters, it doesn't have to be specifically -1 or 1. So instead of if and else, you can simply subtract the numbers.
compare(a, b) {
let comparison = b.essentialPercentage - a.essentialPercentage;
if (comparison == 0) {
comparison = b.skillsNicePercentage - a.skillsNicePercentage;
if (comparison == 0) {
comparison = a.startDate - b.startDate;
}
}
return comparison;
}
If any of the properties are strings rather than numbers, you can use localCompare instead of subtraction.
This tiny function (or the equivalent <=> operator) is perhaps the most obvious lack in the js standard library:
// returns 1 if a > b, -1 if a < b, 0 if a == b
let cmp = (a, b) => (a > b) - (a < b)
Once you have defined it, chained comparisons are very easy:
compare = (a, b) =>
cmp(a.essentialsPercentage, b.essentialsPercentage)
|| cmp(a.skillsNicePercentage, b.skillsNicePercentage)
|| cmp(a.startDate, b.startDate)
If you wanted to you could use a switch statement to keep each one of your cases nice and organized. This would be a "cleaner" method but not necessarily the most correct method. See this thread --> https://stackoverflow.com/a/2312837/11263228
Alternatively, you could create separate functions that will check each of your cases. For example, having a function that takes in a.skillsNicePercentage and b.skillsNicePercentage as parameters and returns true/false. This would be cleaner and also reusable!
You could generalize this in a simpler function that takes an array of field names and sorts the overall array based on those fields, in order. The code below takes some inspiration from the Mongoose database library and allows the - prefix on a field name to sort descending instead of the default ascending order. It also only works for numbers and strings; if you want to support other types, you'll have to extend the code.
function multiSort(fields) {
return (a,b) => {
let result = 0;
for (let i = 0; result === 0 && i < fields.length; ++i) {
let fieldName = fields[i];
if (fieldName.charAt(0) === '-') {
fieldName = fieldName.substring(1);
if (typeof a[fieldName] === 'string') {
result = b[fieldName].localeCompare(a[fieldName]);
}
else {
result = b[fieldName] - a[fieldName];
}
} else {
if (typeof a[fieldName] === 'string') {
result = a[fieldName].localeCompare(b[fieldName]);
}
else {
result = a[fieldName] - b[fieldName];
}
}
}
return result;
};
}
This higher-order function will take an array of fields and return a function that sorts by those fields, in order, for strings and numbers, optionally with a field name prepended by - to sort that field in descending order. You'd use it like this:
someArrayValue.sort(multisort(['essentialsPercentage', 'skillsNicePercentage', '-startDate']));
Based on the logic in your comparison, this should work
function compare (a, b) {
let comparison = a.skillsNicePercentage == b.skillsNicePercentage ? (a.startDate - b.startDate) : b.skillsNicePercentage - a.skillsNicePercentage
let comparison1 = a.essentialsPercentage == b.essentialsPercentage ? b.skillsNicePercentage - a.skillsNicePercentage : comparison
return comparison1;
}
Try
function compare(a, b) {
let c= b.essentialsPercentage - a.essentialsPercentage;
let d= b.skillsNicePercentage - a.skillsNicePercentage;
let e= a.startDate - b.startDate
return Math.sign(c||d||e);
}
function compareNew(a, b) {
let c= b.essentialsPercentage - a.essentialsPercentage;
let d= b.skillsNicePercentage - a.skillsNicePercentage;
let e= a.startDate - b.startDate
return Math.sign(c||d||e);
}
function compareOld(a, b) {
let comparison = 0;
if (a.essentialsPercentage < b.essentialsPercentage) {
comparison = 1;
} else if (a.essentialsPercentage > b.essentialsPercentage) {
comparison = -1;
} else {
if (a.skillsNicePercentage < b.skillsNicePercentage) {
comparison = 1;
} else if (a.skillsNicePercentage > b.skillsNicePercentage) {
comparison = -1;
} else {
if (a.startDate > b.startDate) {
comparison = 1
} else if (a.startDate < b.startDate) {
comparison = -1
}
}
}
return comparison;
}
// TESTS
a={essentialsPercentage:2,skillsNicePercentage:2,startDate:new Date(0)};
tests=[
{essentialsPercentage:2,skillsNicePercentage:2,startDate:new Date(0)},
{essentialsPercentage:2,skillsNicePercentage:2,startDate:new Date(+10000)},
{essentialsPercentage:2,skillsNicePercentage:2,startDate:new Date(-10000)},
{essentialsPercentage:2,skillsNicePercentage:2,startDate:new Date()},
{essentialsPercentage:2,skillsNicePercentage:3,startDate:new Date()},
{essentialsPercentage:2,skillsNicePercentage:1,startDate:new Date()},
{essentialsPercentage:3,skillsNicePercentage:1,startDate:new Date()},
{essentialsPercentage:1,skillsNicePercentage:1,startDate:new Date()},
]
tests.map(b=> console.log(`old: ${compareNew(a,b)} new:${ compareOld(a,b)}`));

JS function switch between either greater than or less than

I have a function that is quite long and at the moment I need a duplicate of the function where the only difference is it asks greater than rather than less than. So the only difference is > or <.
Is there any (non messy) way to make this kind of function just one function instead of the two here?
function(value, modifier) {
let result;
if (value > modifier) result = 10;
return result;
}
function(value, modifier) {
let result;
if (value < modifier) result = 10;
return result;
}
So basically I need a conditional greater than/less than sign.
EDIT: To be clear, in an ideal world I'd like to do this:
myFunction(10, 5, >)
EDIT: Adding one part of my actual function to make things clearer. I was hoping there might be real simple way to do it but it seems like perhaps not so maybe part of the actual function might help things:
function getEdges(colour) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
if (data[0] < colour) {
edge.push(y);
break;
}
}
}
return edge;
}
And there is another almost identical function like this where the only difference is the line if (data[0] > colour) { is greater than rather than less than.
If the only difference between the two functions is the comparison, then you can just extract that and make it a parameter
function getEdges(colour, comparator) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
if (comparator(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
//call with less than
getEdges(someColour, (a, b) => a < b)
//call with greater than
getEdges(someColour, (a, b) => a > b)
You can also keep your logic in one function and derive two more from it. This way you don't need to maintain multiple code blocks and you still get two explicit calls:
Using partial application with .bind:
function getEdges(comparator, colour) {
// ^ ^ the two parameters are swapped
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
if (comparator(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
//partial application
const getEdgesLessThan = getEdges.bind(null, (a, b) => a < b);
const getEdgesGreaterThan = getEdges.bind(null, (a, b) => a > b);
getEdgesLessThan(someColour)
getEdgesGreaterThan(someColour)
Using a curried function:
function getEdges(comparator) {
// ^---------
return function(colour) {// | taking two parameters
// ^ ----------
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
if (comparator(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
}
//currying
const getEdgesLessThan = getEdges((a, b) => a < b);
const getEdgesGreaterThan = getEdges((a, b) => a > b);
getEdgesLessThan(someColour)
getEdgesGreaterThan(someColour)
How about passing the condition function as a third parameter?
function getEdges(colour, condition) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
if (condition(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
Call the function and pass required condition.
for lesser than: getEdges(color, (data, color) => color < data);
for greater than: getEdges(color, (data, color) => color > data);
EDIT: To be clear, in an ideal world I'd like to do this:
myFunction(10, 5, >)
You can get something very similar:
const lt = (x, y) => x < y;
const gt = (x, y) => x > y;
function foo(value, modifier, compare) {
let result;
if (compare(value, modifier)) result = 10;
return result;
}
console.log(foo(2, 3, lt)); // 10
console.log(foo(3, 2, gt)); // 10
Using your second example:
const lt = (x, y) => x < y;
const gt = (x, y) => x > y;
const width = 3;
const height = 3;
const getData = (x, y) => [height * x + y];
console.log(getEdges(3, lt)); // [2]
console.log(getEdges(3, gt)); // [2,2]
function getEdges(colour, compare) {
const edge = [];
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x, y);
if (compare(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
Hope that helps.
You could keep the operator and just swap the terms according to your needs:
function getEdges(colour, operator) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
const [a, b] = operator == '<' ? [data[0], colour] : [colour, data[0]];
if (a < b) {
edge.push(y);
break;
}
}
}
return edge;
}
EDIT:
Ok, so if you want keep things simple and in separated functions, with minimal code changes you could implement it like this:
function getEdges(colour, operator = '<') {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
const [a, b] = operator == '<' ? [data[0], colour] : [colour, data[0]];
if (a < b) {
edge.push(y);
break;
}
}
}
return edge;
}
The signature getEdges(colour, operator = '<') makes the operator parameter optional. If you don't pass it to the function, it'll assume a default value of '<' so that you won't to have to change anything in your existing code. Then, you could make a second function that will reuse the original one, just with a different parameter:
function getEdgesGreaterThan(colour) {
return getEdges(colour, '>');
}
And there you have it! Hope it helps!
Based on the conversation in the comments, the purpose is to re-use the getEdges function for when a greater than or lower than comparison is needed. I've added a second parameter to indicate this, with it set to false as the default case. The if statement is dual-purpose in the sense that it makes a greater than comparison when the isGreater flag is set to true and a less than comparison when the isGreater flag is set to false. The rest of the logic is re-used verbatim with no duplication.
function getEdgesInner(colour, isGreater = false) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
if ((isGreater && data[0] > colour) || (!isGreater && data[0] < colour))
edge.push(y);
break;
}
}
}
return edge;
}
/* Public Api */
function getGreaterEdges(colour) { return getEdges(colour) }
function getLesserEdges(colour) { return getEdges(colour, true) }
You can try these ways. just like you wanted in your original sample! With short, stylish and beauty ideas:
mathematical trick:
function func(value, modifier, sign) {
let result, fc=sign=="<"?1:-1;
if (value*fc< modifier*fc) result = 10;
return result;
}
A useful JS feature:
function func(value, modifier, sign) {//this is slower probably
let result;
if (eval(value+sign+modifier)) result = 10;
return result;
}
Usage (both top ways):
console.log(func(1, 2, "<"));
Passing a delegate function (for compare):
function func(value, modifier, compF) {
let result;
if (compF(value, modifier)) result = 10;
return result;
}
Usage :
console.log(func(1, 2, function(v, m){return v<m;}/*or equivalent lambda function*/));
function(value, modifier, type) {
let result;
if(type == ">") {
if (value > modifier) result = 10;
} else {
if (value < modifier) result = 10;
}
return result;
}
and pass type as a string "<" or ">"
You should add 3rd argument which takes the condition, for example, string "gt" for Grater Than(>) and "lt" for Less Than(<)
function(value, modifier, cond){
if(cond === "gt") return value > modifier ? 10 : undefined;
else if(cond === "lt") return value < modifier ? 10 : undefined;
}
EDIT: I came up with a better solution, suitable for your updated question.
let check = (value, modifier, cond)=>eval(value + cond + modifier) ? 10 : undefined
here you pass "<" or ">" for cond parameter.
EDIT 2:
function getEdges(colour,cond) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
let shoudPush = eval(data[0] + cond + colour) ? true : false
if(shouldPush) {
edge.push(y);
break;
}
}
}
return edge;
}
EDIT: To be clear, in an ideal world I'd like to do this:
myFunction(10, 5, >)
You can almost do that with using eval. Although I'm a little confused about your result since it potentially returns undefined.
var myFunction = function(value, modifier, operation) {
var evalResult = eval(value + operation + modifier)
// I like ternary expression but could also be written as an if/else statement
return evalResult ? 10 : undefined
}
myFunction(10, 5, '<') // outputs undefined
myFunction(10, 5, '>') // outputs 10
Just be aware that eval can be dangerous.
https://medium.com/#eric_lum/the-dangerous-world-of-javascripts-eval-and-encoded-strings-96fd902af2bd
EDIT
Hey I made a codepen in response to your edited answer.
https://codepen.io/springborg/pen/ydayYa
I think this is what most closely matches your proposed syntax. Although I would probably advise against using eval unless you are absolutely certain no vulnerability will be exposed.
My recommendation is using Aditya Bhave answer :)
https://stackoverflow.com/a/56649540/1303205
If reusability is your ultimate goal, here's a curried example of how one might do this (somewhat functional-programming-stylez).
This approach allows greater reuse of the code. You might want to compare things at two points in your code, and return different values according to context.
let functionalComparer = comp => modifier => result => value => {
return comp(value, modifier) ? result: undefined
}
// curry the crap out of it!
let gt = (x,y) => x > y
let gtComp = functionalComparer(gt)
let gt100 = gtComp(100)
let whenGt100ReturnString = gt100("more than 100!")
console.log(`
with value 99: ${whenGt100ReturnString(99)},
with value 101: ${whenGt100ReturnString(101)}
`)
let lt = (x,y) => x < y
let whenLt50ReturnFoo = functionalComparer(lt)(50)("FOO")
console.log(`
with value 49: ${whenLt50ReturnFoo(49)},
with value 51: ${whenLt50ReturnFoo(51)}
`)

jQuery TypeError: includes is not a function in firefox [duplicate]

right here is a block of my code. It works perfect in fireFox and Chrome. But not in IE. I get the error "Object doesn't support property or method 'includes'"
function rightTreeSwapfunc2() {
if ($(".right-tree").css("background-image").includes("stage1") == true) {
$(".right-tree").css({
backgroundImage: "url(/plant-breeding/img/scenes/plant-breeding/stage5.jpg)"
})
} else {
$(".right-tree").css({
backgroundImage: "url(/plant-breeding/img/scenes/plant-breeding/stage3.jpg)"
})
}
}
I could change it up a bit and use vanilla JS and do:
document.getElementById("right-tree").classList.contains
But I would rather see if there is a way to get it to work in IE before changing the JS and editing the HTML and CSS.
If you look at the documentation of includes(), most of the browsers don't support this property.
You can use widely supported indexOf() after converting the property to string using toString():
if ($(".right-tree").css("background-image").indexOf("stage1") > -1) {
// ^^^^^^^^^^^^^^^^^^^^^^
You can also use the polyfill from MDN.
if (!String.prototype.includes) {
String.prototype.includes = function() {
'use strict';
return String.prototype.indexOf.apply(this, arguments) !== -1;
};
}
IE11 does implement String.prototype.includes so why not using the official Polyfill?
Source: polyfill source
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
In my case i found better to use "string.search".
var str = "Some very very very long string";
var n = str.search("very");
In case it would be helpful for someone.
Here is solution ( ref : https://www.cluemediator.com/object-doesnt-support-property-or-method-includes-in-ie )
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function (searchElement, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
if (sameValueZero(o[k], searchElement)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
});
}
import 'core-js/es7/array'
into polyfill.ts worked for me.

Sort empty or null to bottom always

The following is a natural sort function I pulled from somewhere I forget exactly. I'm looking to modify it so that empty or null values always sort to the bottom regardless of asc/desc.
Here is what I have right now:
function gridNaturalSorter(a, b) {
if(a[sortcol])
a = a[sortcol].replace(/<(?:.|\n)*?>/gm, '');
if(b[sortcol])
b = b[sortcol].replace(/<(?:.|\n)*?>/gm, '');
if(b)
b = b.toString().substr(0, 15);
if(a)
a = a.toString().substr(0, 15);
var re = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi,
sre = /(^[ ]*|[ ]*$)/g,
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
hre = /^0x[0-9a-f]+$/i,
ore = /^0/,
i = function(s) {
return gridNaturalSorter.insensitive && (''+s).toLowerCase() || ''+s
},
// convert all to strings strip whitespace
x = i(a).replace(sre, '') || '',
y = i(b).replace(sre, '') || '',
// chunk/tokenize
xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
// numeric, hex or date detection
xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
oFxNcL, oFyNcL;
// first try and sort Hex codes or Dates
if (yD)
if ( xD < yD ) return -1;
else if ( xD > yD ) return 1;
// natural sorting through split numeric strings and default strings
for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
// handle numeric vs string comparison - number < string - (Kyle Adams)
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
return (isNaN(oFxNcL)) ? 1 : -1;
}
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
else if (typeof oFxNcL !== typeof oFyNcL) {
oFxNcL += '';
oFyNcL += '';
}
if (oFxNcL < oFyNcL)
return -1;
if (oFxNcL > oFyNcL)
return 1;
}
return 0;
}
If you know how to implement multiple comparators and a comparator that sorts null to bottom that's quite easy.
To implement multiple comparators, you just have to return the result of the first comparator that doesn't return 0.
Here I also created a withComparators helper function that allows to compose multiple comparators together. If you understand this code you will be able to easily come up with your own solution for your specific problem.
Note that your gridNaturalSorter function is a comparator just like nullsToBottom is in my example.
E.g.
var items = ['test', null, 'test1', 'test3', null, 'test4'];
items.sort(withComparators(nullsToBottom, textAsc));
//["test", "test1", "test3", "test4", null, null]
function nullsToBottom(a, b) {
return a === b? 0 : a === null? 1 : -1;
}
function textAsc(a, b) {
return a < b? -1 : +(a > b);
}
function withComparators() {
var comparators = arguments;
return function (a, b) {
var len = comparators.length, i = 0, result;
for (; i < len; i++) {
result = comparators[i](a, b);
if (result) return result;
}
return 0;
};
}

Categories

Resources