Ignore non-numeric characters in numbers for DataTables sort - datatables

I'm creating a custom sort plugin for DataTables which will sort numeric columns which contain non-numeric rows as well. I got the part where it puts the 'N/A' rows at the bottom but cannot seem to figure out, how to make it ignore the commas in the numbers.
For example:
$12,443.00
362,123,231
N/A
N/A
null
34,242.42
23,234
null
The code below manages to ignore everything except for the commas in the numbers.
Code:
function numeric_sort(a, b, high) {
var reg = /[+-]?((\d+(\.\d*)?)|\.\d+)([eE][+-]?[0-9]+)?/;
a = a.match(reg);
a = a !== null ? parseFloat(a[0]) : high;
b = b.match(reg);
b = b !== null ? parseFloat(b[0]) : high;
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
"numeric-empty-bottom-asc": function (a, b) {
return numeric_sort(a, b, Number.POSITIVE_INFINITY);
},
"numeric-empty-bottom-desc": function (a, b) {
return numeric_sort(a, b, Number.NEGATIVE_INFINITY) * -1;
}
} );
I got the code from: http://jsfiddle.net/6qmkY/
Any help will be appreciated.

You can just use replace() to remove commas.
function sortNumbersIgnoreText(a, b, high) {
var reg = /[+-]?((\d+(\.\d*)?)|\.\d+)([eE][+-]?[0-9]+)?/;
a = a.replace(/,/g, '');
a = a.match(reg);
a = a !== null ? parseFloat(a[0]) : high;
b = b.replace(/,/g, '');
b = b.match(reg);
b = b !== null ? parseFloat(b[0]) : high;
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}
See updated jsFiddle for code and demonstration.

You could replace all non numeric chars including commas (excluding the decimal).
function sortNumbersIgnoreText(a, b, high) {
a = a.replace(/[^0-9\.]+/g, '');
a = (a !== null && a !== '') ? parseFloat(a) : high;
b = b.replace(/[^0-9\.]+/g, '');
b = (b !== null && b !== '') ? parseFloat(b) : high;
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}

Related

TypeError: Cannot read properties of undefined (reading 'type') at eval ... at Array.sort (<anonymous>)

I need your help with this error I am facing colleagues. I am new to vue so I am finding it quite difficult to solve the error though I what what exactly is causing the error. I am creating a datatable in vue and I am trying to achieve data sorting with this tutorial I am following but end up getting the following error:
TypeError: Cannot read properties of undefined (reading 'type')
computed: {
filteredAccommodations(){
let accommodations = this.accommodations;
if (this.search) {
accommodations = accommodations.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(this.search.toLowerCase()) > -1;
})
});
}
let sortKey = this.sortKey;
let order = this.sortOrders[sortKey] || 1;
if(sortKey){
accommodations = accommodations.slice().sort((a, b) => {
let index = this.getIndex(this.columns, 'name', sortKey);
a = String(a[sortKey]).toLowerCase();
b = String(b[sortKey]).toLowerCase();
if (this.columns[index].type && this.columns[index].type === 'date') {
return (a === b ? 0 : new Date(a).getTime() > new Date(b).getTime() ? 1 : -1) * order;
} else if (this.columns[index].type && this.columns[index].type === 'number') {
return (+a === +b ? 0 : +a > +b ? 1 : -1) * order;
} else {
return (a === b ? 0 : a > b ? 1 : -1) * order;
}
});
}
return accommodations;
},
paginatedAccommodations(){
return this.paginate(this.filteredAccommodations, this.length, this.pagination.currentPage);
}
},
The reason for your error is because the value of this.columns[index] is a null value ,Adding a null check in ur if loop might help you solve this but I suggest you to check for the reason of null value.
computed: {
filteredAccommodations() {
let accommodations = this.accommodations;
if (this.search) {
accommodations = accommodations.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(this.search.toLowerCase()) > -1;
})
});
}
let sortKey = this.sortKey;
let order = this.sortOrders[sortKey] || 1;
if (sortKey) {
accommodations = accommodations.slice().sort((a, b) => {
let index = this.getIndex(this.columns, 'name', sortKey);
a = String(a[sortKey]).toLowerCase();
b = String(b[sortKey]).toLowerCase();
if (this.columns[index] && this.columns[index].type && this.columns[index].type === 'date') {
return (a === b ? 0 : new Date(a).getTime() > new Date(b).getTime() ? 1 : -1) * order;
} else if (this.columns[index] && this.columns[index].type && this.columns[index].type === 'number') {
return (+a === +b ? 0 : +a > +b ? 1 : -1) * order;
} else {
return (a === b ? 0 : a > b ? 1 : -1) * order;
}
});
}
return accommodations;
},
paginatedAccommodations() {
return this.paginate(this.filteredAccommodations, this.length, this.pagination.currentPage);
}
},

How to compare two routes by hand

How can I manually (programmatically) compare two routes and find out if they are same? (if router-link-active or router-link-exact-active would be present)
Generally i need this sort of a function
/*
#params route1, route2 : Route
*/
function isActivated(route1, route2) {
/* comparing them somehow */
return {
exactActive,
active
};
}
Use Case:
I have a NestedLink.vue component which is wrapper over router-link.
It takes to prop just as router-link (and passes it down to child router-link). If current route is active, nested links will apear nearby.
My approach:
function isActivated(route1, route2) {
if (
route1.matched.some(record =>
record.regex.test(route2.fullPath)
)
) {
return { exactActive: true };
}
return { exactActive: false };
}
It may tell when routes are exact-active but not for not-exact-active.
This is the code, used inside router-link component.
const START = '/';
const trailingSlashRE = /\/?$/;
function isObjectEqual (a, b) {
if ( a === void 0 ) a = {};
if ( b === void 0 ) b = {};
// handle null value #1566
if (!a || !b) { return a === b }
var aKeys = Object.keys(a);
var bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) {
return false
}
return aKeys.every(function (key) {
var aVal = a[key];
var bVal = b[key];
// check nested equality
if (typeof aVal === 'object' && typeof bVal === 'object') {
return isObjectEqual(aVal, bVal)
}
return String(aVal) === String(bVal)
})
}
function isSameRoute (a, b) {
if (b === START) {
return a === b
} else if (!b) {
return false
} else if (a.path && b.path) {
return (
a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
a.hash === b.hash &&
isObjectEqual(a.query, b.query)
)
} else if (a.name && b.name) {
return (
a.name === b.name &&
a.hash === b.hash &&
isObjectEqual(a.query, b.query) &&
isObjectEqual(a.params, b.params)
)
} else {
return false
}
}
So, here's how to use it inside component:
// OR JUST A STRING '/home'
let link = { name: 'home' }
// will return true of false
let result = isSameRoute(this.$router.resolve(link).resolved, this.$route)

Natural Sorting Datatable.js

Natural sorting in datatable.js
Using this javascript fuctionnaturalSort(a, b) we can any datatype column sorting
Example: we want sorting is datatable like 1,101,99,88,103
We can use this and result will be 1,88,99,101,103
function naturalSort(a, b) {
// setup temp-scope variables for comparison evauluation
var x = a.toString().toLowerCase() || '', y = b.toString().toLowerCase() || '',
nC = String.fromCharCode(0),
xN = x.replace(/([-]{0,1}[0-9.]{1,})/g, nC + '$1' + nC).split(nC),
yN = y.replace(/([-]{0,1}[0-9.]{1,})/g, nC + '$1' + nC).split(nC),
xD = (new Date(x)).getTime(), yD = (new Date(y)).getTime();
// natural sorting of dates
if (xD && yD && xD < yD)
return -1;
else if (xD && yD && 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++)
if ((parseFloat(xN[cLoc]) || xN[cLoc]) < (parseFloat(yN[cLoc]) || yN[cLoc]))
return -1;
else if ((parseFloat(xN[cLoc]) || xN[cLoc]) > (parseFloat(yN[cLoc]) || yN[cLoc]))
return 1;
return 0;
}
jQuery.fn.dataTableExt.oSort['natural-asc'] = function (a, b) {
return naturalSort(a, b);
};
jQuery.fn.dataTableExt.oSort['natural-desc'] = function (a, b) {
return naturalSort(a, b) * -1;
};
Add aocolumns in datatable properties and put sType is natual.
aoColumns: [
{ "sType": "natural" }
],

dgrid custom sort with secondary sort column

I'm currently using a custom sort function on a dgrid (pasted below). It doesn't change sorting drastically, just sorts one particular column uniquely and sorts the others case-insensitive. I'd like to add a secondary sort by a column named "scheduled" to be added to the sort when any other column is sorted. I'm just not sure how to go about it. I've seen how to override the sort to sort by two columns, but not when a custom sort is in play. The secondary sort would always be there, not matter what other column is clicked.
For reference I'm running dojo 1.10 and dgrid 1.0. Data is coming from a RequestMemory DStore and I'd really rather this sort happen on the grid rather than back at the store level. Any help would be appreciated.
currGrid.on('dgrid-sort', function (event) {
event.preventDefault();
var sort = event.sort[0];
currGrid.set('sort', function (a, b) {
if (sort.property == "thisField") {
//special sort for thisField
if (a[sort.property] !== 'undefined' && typeof a[sort.property] == "string") {
var colorA = a[sort.property].split("|");
var aValue = colorA[0].toLowerCase();
}
if (b[sort.property] !== 'undefined' && typeof b[sort.property] == "string") {
var colorB = b[sort.property].split("|");
var bValue = colorB[0].toLowerCase();
}
if (String(aValue) == String(bValue)) {
var result = 0;
} else if (dojo.string.trim(aValue) == "") {
var result = true ? 1 : -1;
} else if (dojo.string.trim(bValue) == "") {
var result = true ? -1 : 1;
} else {
var result = aValue > bValue ? 1 : -1;
}
return result * (sort.descending ? -1 : 1);
} else {
//Sort for all other fields same as always (except toLowerCase)
if (a[sort.property] !== 'undefined' && typeof a[sort.property] == "string") {
var aValue = a[sort.property].toLowerCase();
} else {
var aValue = "";
}
if (b[sort.property] !== 'undefined' && typeof b[sort.property] == "string") {
var bValue = b[sort.property].toLowerCase();
} else {
var bValue = "";
}
var result = aValue > bValue ? 1 : -1;
return result * (sort.descending ? -1 : 1);
}
});
currGrid.updateSortArrow(event.sort, true);
});
currGrid.startup();
You could do something like below.
currGrid.on('dgrid-sort', function (event) {
event.preventDefault();
var sortSet = [];
sortSet.push(event.sort[0]);
sortSet.push({property: "scheduled"});
currGrid.set('sort', function (a, b) {
var aValue, bValue, result = 0;
for(var i = 0; i < sortSet.length; i++){
var sort = sortSet[i];
if (sort.property == "thisField") {
//special sort for thisField
if (a[sort.property] !== 'undefined' && typeof a[sort.property] == "string") {
var colorA = a[sort.property].split("|");
aValue = colorA[0].toLowerCase();
}
if (b[sort.property] !== 'undefined' && typeof b[sort.property] == "string") {
var colorB = b[sort.property].split("|");
bValue = colorB[0].toLowerCase();
}
if (String(aValue) == String(bValue)) {
result = 0;
} else if (dojo.string.trim(aValue) == "") {
result = true ? 1 : -1;
} else if (dojo.string.trim(bValue) == "") {
result = true ? -1 : 1;
} else {
result = aValue > bValue ? 1 : -1;
}
return result * (sort.descending ? -1 : 1);
} else {
//Sort for all other fields same as always (except toLowerCase)
if (a[sort.property] !== 'undefined' && typeof a[sort.property] == "string") {
aValue = a[sort.property].toLowerCase();
} else {
aValue = "";
}
if (b[sort.property] !== 'undefined' && typeof b[sort.property] == "string") {
bValue = b[sort.property].toLowerCase();
} else {
bValue = "";
}
//You need this check here
if(aValue != bValue){
result = aValue > bValue ? 1 : -1;
return result * (sort.descending ? -1 : 1);
}
}
}
return 0;
});
currGrid.updateSortArrow(event.sort, true);
});
currGrid.startup();
I have some concerns about your code, the variables result, aValue and bValue are all local within the if statement and yet they are being used outside the statement. It could result in wrong results if some other variables are defined with the same name in global space. So I have modified them.
So the second section you needed to check if aValue == bValue to return 0.

Need a flexible currency filter in VueJS

I need to modify the currency filter to be able to dictate number of decimal places … i need 0, 2, and 4 decimal places in currency in different places … I am thinking a “ | flexCurrency: 4” but can’t find the necessary documentation to figure out how to override the currency filter. The filter I am in imagining in Angular looks like this:
.filter('flexCurrency', flexCurrencyFilter)
function flexCurrencyFilter($filter) {
return function (input, decPlaces) {
decPlaces = decPlaces || 0;
// Check for invalid inputs
if (isNaN(input) || !input || Math.abs(input) === 0 || (Math.abs(input) > 0 && Math.abs(input) < 1)) {
return '-';
}
var out = input;
//Deal with the minus (negative numbers)
var minus = out < 0;
out = Math.abs(out);
out = $filter('number')(out, decPlaces);
// Add the minus and the symbol
if (minus) {
return '( $' + out + ')';
} else {
return '$' + out;
}
};
}
The currency filter is in the source code, just adapt it to take an extra arg. This should work:
flexCurrency (value, currency, decimals) {
value = parseFloat(value)
if (!isFinite(value) || (!value && value !== 0)) return ''
currency = currency != null ? currency : '$'
var stringified = Math.abs(value).toFixed(decimals)
var _int = stringified.slice(0, -1 - decimals)
var i = _int.length % 3
var head = i > 0
? (_int.slice(0, i) + (_int.length > 3 ? ',' : ''))
: ''
var _float = stringified.slice(-1 - decimals)
var sign = value < 0 ? '-' : ''
return sign + currency + head +
_int.slice(i).replace(digitsRE, '$1,') +
_float
},