Related
I need to sort a list of data by alphabetical order but in A-a-Z-z, were the name Antony comes before the name antony and Zelda comes before zelda so the list looks like this:
- Abigail
- Antony
- abigail
- antony
- Zelda
- zelda
The basic
list.sort(function (a, b) {
if (a.name > b.name) return -1;
if (a.name < b.name) return 1;
return 0;
});
is producing a list like this:
Abigail
Antony
Zelda
abigail
antony
zelda
preferable language: Javascript
There is a built-in for that:
let list = ["abigail", "Antony", "Abigail", "antony", "Zelda", "zelda"];
list.sort((a, b) =>
a.localeCompare(b, "en", { caseFirst: "upper" })
);
console.log(list);
EDIT: maybe you want this?
let list = ["abigail", "Antony", "Abigail", "antony", "Zelda", "zelda"];
const compareUpperFirst = (a, b) => {
if (a === "" && bb === "") return 0;
if (a === "") return -1;
if (b === "") return 1;
let aa = a.charAt(0);
let aal = aa.toLowerCase();
let bb = b.charAt(0);
let bbl = bb.toLowerCase();
if (aal < bbl) return -1;
if (aal > bbl) return 1;
if (aa < bb) return -1;
if (aa > bb) return 1;
return compareUpperFirst(a.substr(1), b.substr(1));
};
list.sort(compareUpperFirst);
console.log(list);
I am trying to create a sorting function that sorts a nested array of objects while giving a key dynamically (with different depths).
sortByKey(array, key){
var splitKey = key.split(".");
if(splitKey.length = 2){
return array.sort(function(a, b) {
var x = a[splitKey[0]][splitKey[1]]; var y = b[splitKey[0]][splitKey[1]];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
} else {
return array.sort(function(a, b) {
var x = a[key]; var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
}
I want to get rid of the if - else and use a for loop instead. The goal is that the function works with 'name', 'name.first' and 'name.first.another' (as an example). Is there a way to do this dynamically?
In other words, I want to use the same function with different arrays. So with one array I want to sort it calling sortByKey(array1, 'name') and with another sortByKey(array2, 'location.address') and maybe with a third sortByKey(array3, 'location.address.postalcode') or something like that.
Extract property extracting function
function prop(key) {
var keys = key.split('.');
return keys.reduce.bind(keys, function(obj, name) {
return obj[name]
})
}
and use it to well extract values :)
sortByKey(array, key){
var getKey = prop(key);
return array.sort(function(a, b){
var x = getKey(a); var y = getKey(b);
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
})
}
I think you mean something like this:
function sortByKey(array, key){
var splitKey = key.split(".");
return array.sort(function(a, b) {
var ta = a;
var tb = b;
for (var i=0; i<splitKey.length; i++) {
ta = ta[splitKey[i]];
};
/// return ((a < b) ? -1 : ((a > b) ? 1 : 0)); // Too complex ;-)
return a - b;
});
};
Your problem is a misused assignment, where it should be a comparison.
if (splitKey.length === 2) {
// ^^^
A shorter approach could use Array#reduce.
function sortByKey(array, key) {
var getValue = function (o, k) { return o[k]; },
keys = key.split(".");
return array.sort(function (a, b) {
return keys.reduce(getValue, a) - keys.reduce(getValue, b);
});
}
var array = [{ a: 5, b: { c: 2 } }, { a: 7, b: { c: 1 } }, { a: 1, b: { c: 3 } }];
sortByKey(array, 'a');
console.log(array);
sortByKey(array, 'b.c');
console.log(array);
ES6
function sortByKey(array, key) {
const getValue =
(keys => object => keys.reduce((o, k) => o[k], object))
(key.split('.'));
return array.sort((a, b) => getValue(a) - getValue(b));
}
var array = [{ a: 5, b: { c: 2 } }, { a: 7, b: { c: 1 } }, { a: 1, b: { c: 3 } }];
sortByKey(array, 'a');
console.log(array);
sortByKey(array, 'b.c');
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am using this function to sort an array based on object key:
function keysrt(arr, key, reverse) {
var sortOrder = 1;
if(reverse){
sortOrder = -1;
}
return arr.sort(function(a, b) {
var x = a[key],
y = b[key];
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
It works well with this type of array, where key is on the first level:
var a = [
{ id: 0, last: 'Anne'},
{ id: 1, last: 'Odine'},
{ id: 2, last: 'Caroline'}
]
keysrt(a, 'last');
How can I make it work with this example, where title key is nested?
var b = [
{ id: 0, last: 'Anne', data:{title: 'habc'}},
{ id: 1, last: 'Odine', data:{title: 'asdf'}},
{ id: 2, last: 'Prentice', data:{title: 'tzuio'}}
]
keysrt(b, 'title');
For this idea the "key" variable changes into an array of keys: Then you specify the "path" to the nested value you want to sort on.
function keysrt(arr, keyArr, reverse) {
var sortOrder = 1;
if(reverse)sortOrder = -1;
return arr.sort(function(a, b) {
var x=a,y=b;
for (var i=0; i < keyArr.length; i++) {
x = x[keyArr[i]];
y = y[keyArr[i]];
}
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
keysrt(b,['data','title']);
If you are ready to change the function signature and the function call, here is a simple solution-
function keysrt(arr, prop, key, reverse) {
var sortOrder = 1;
if(reverse)sortOrder = -1;
return arr.sort(function(a, b) {
var x = a[prop][key]; var y = b[prop][key];
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
var b = [
{ id: 0, last: 'Anne', data:{title: 'habc'}},
{ id: 1, last: 'Odine', data:{title: 'asdf'}},
{ id: 2, last: 'Prentice', data:{title: 'tzuio'}}
]
keysrt(b,'data', 'title');
Here, prop represents the outer object, key would represent the nested key.
So, var y = b[prop][key] would basically mean you are accessing b.data.title
Hope it helps :) Happy coding!
If you need to make it generic, I think you can pass in a function that will retrieve the value from array item for comparison:
function keysrt(arr, reverse, getValueFn) {
var sortOrder = 1;
if(reverse)sortOrder = -1;
return arr.sort(function(a, b) {
var x = getValueFn(a); var y = getValueFn(b);
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
So that you can use it like:
keysrt(b, true, function(a){return a.data.title})
You can get working example with following code:
function keysrt(arr, key, reverse) {
var sortOrder = reverse ? -1 : 1;
return arr.sort(function(a, b) {
var x,y;
if(typeof a[key] !== "undefined") {
x = a[key];
y = b[key];
} else {
for(var prop in a) {
if(a[prop][key] !== "undefined") {
x = a[prop][key];
y = b[prop][key];
}
}
}
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
but I would propose more generic solution
function keysrt(arr, path, reverse) {
var sortOrder = reverse ? -1 : 1;
var pathSplitted = path.split(".");
if(arr.length <= 1) {
return arr;
}
return arr.sort(function(a, b) {
var x = a;
var y = b;
pathSplitted.forEach(function(key) {
x = x[key];
y = y[key];
});
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
in which one can provide a path to sorting field like this
var sorted = keysrt(b, 'data.title');
Demo: http://jsbin.com/cosugawoga/edit?js,console
To find a nested property value, any number of levels down, you can use JSON.stringify as a way to walk the object:
function get_nested_value(obj, prop) {
var result;
JSON.stringify(obj, function(key, value) {
if (key === prop) result = value;
});
return result;
}
Now:
function keysrt(arr, key, reverse) {
var sortOrder = 1;
if(reverse){
sortOrder = -1;
}
return arr.sort(function(a, b) {
var x = get_nested_value(a, key);
y = get_nested_value(b, key);
return sortOrder * ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
I need to sort an array by his inside first element data .
my array looks something like that
arr = [[0,"lol"][6,"yo"][5,"comon"]]
After the sorting I need it to be like that :
[[0,"lol"][5,"comon"][6,"yo"]]
0 , 5 , 6 suppose to order the cells and they data they have is irrelevent.
Thanks.
You can try something like this...
Live Demo
I made some changes...
Corrected the mistake :
// before that I'm checking arrays and not a values of it...
(!isNaN(a) && !isNaN(b)) to (!isNaN(a[0]) && !isNaN(b[0]))
and to ignore case...
aa = a[0].toString().toLowerCase();
bb = b[0].toString().toLowerCase();
===========================================================
arr.sort(function (a, b) {
var aa, bb;
if (!isNaN(a[0]) && !isNaN(b[0])) {
return a[0] - b[0];
} else {
aa = a[0].toString().toLowerCase();
bb = b[0].toString().toLowerCase();
return (aa == bb) ? 0 : (aa < bb) ? -1 : 1;
}
});
jsfiddle Link
var arr = [[0,"lol"],[6,"yo"],[5,"comon"]];
arr.sort(function(a, b) {
return a[0] - b[0];
});
Use this code to sort your array..
var arr = [[0,"lol"],[6,"yo"],[5,"comon"]];
arr.sort(function(a, b) {
if (a[0] == b[0]) {
return 0;
} else {
return a[0] < b[0] ? -1 : 1;
}
});
I have a function which sorts by name currently and an array of value / key pairs.
I wonder how can I pass the key on which sort is being performed so I can call the same function every time like so:
var arr = [{name:'bob', artist:'rudy'},
{name:'johhny', artist:'drusko'},
{name:'tiff', artist:'needell'},
{name:'top', artist:'gear'}];
sort(arr, 'name'); //trying to sort by name
sort(arr, 'artist'); //trying to sort by artist
function sort(arr) {
arr.sort(function(a, b) {
var nameA=a.name.toLowerCase(), nameB=b.name.toLowerCase();
if (nameA < nameB) //sort string ascending
return -1;
if (nameA > nameB)
return 1;
return 0; //default return value (no sorting)
});
}
Array.prototype.sortOn = function(key){
this.sort(function(a, b){
if(a[key] < b[key]){
return -1;
}else if(a[key] > b[key]){
return 1;
}
return 0;
});
}
var arr = [{name:'bob', artist:'rudy'},{name:'johhny', artist:'drusko'},{name:'tiff', artist:'needell'},{name:'top', artist:'gear'}];
arr.sortOn("name");
arr.sortOn("artist");
[edit 2020/08/14] This was rather an old answer and not very good as well, so simplified and revised.
Create a function that returns the sorting lambda (the Array.prototype.sort callback that does the actual sorting). That function can receive the key name, the kind of sorting (string (case sensitive or not) or numeric) and the sorting order (ascending/descending). The lambda uses the parameter values (closure) to determine how to sort.
const log = (...strs) =>
document.querySelector("pre").textContent += `\n${strs.join("\n")}`;
const showSortedValues = (arr, key) =>
` => ${arr.reduce((acc, val) => ([...acc, val[key]]), [])}`;
// the actual sort lamda factory function
const sortOnKey = (key, string, desc) => {
const caseInsensitive = string && string === "CI";
return (a, b) => {
a = caseInsensitive ? a[key].toLowerCase() : a[key];
b = caseInsensitive ? b[key].toLowerCase() : b[key];
if (string) {
return desc ? b.localeCompare(a) : a.localeCompare(b);
}
return desc ? b - a : a - b;
}
};
// a few examples
const onNameStringAscendingCaseSensitive =
getTestArray().sort( sortOnKey("name", true) );
const onNameStringAscendingCaseInsensitive =
getTestArray().sort( sortOnKey("name", "CI", true) );
const onValueNumericDescending =
getTestArray().sort( sortOnKey("value", false, true) );
// examples
log(`*key = name, string ascending case sensitive`,
showSortedValues(onNameStringAscendingCaseSensitive, "name")
);
log(`\n*key = name, string descending case insensitive`,
showSortedValues(onNameStringAscendingCaseInsensitive, "name")
);
log(`\n*key = value, numeric desc`,
showSortedValues(onValueNumericDescending, "value")
);
function getTestArray() {
return [{
name: 'Bob',
artist: 'Rudy',
value: 23,
}, {
name: 'John',
artist: 'Drusko',
value: 123,
}, {
name: 'Tiff',
artist: 'Needell',
value: 1123,
}, {
name: 'Top',
artist: 'Gear',
value: 11123,
}, {
name: 'john',
artist: 'Johanson',
value: 12,
}, ];
}
<pre></pre>
function keysrt(key) {
return function(a,b){
if (a[key] > b[key]) return 1;
if (a[key] < b[key]) return -1;
return 0;
}
}
someArrayOfObjects.sort(keysrt('text'));
Make your life easy and use a closure
https://stackoverflow.com/a/31846142/1001405
You can see the working example here
var filter = 'name', //sort by name
data = [{name:'bob', artist:'rudy'},{name:'johhny', artist:'drusko'},{name:'tiff', artist:'needell'},{name:'top', artist:'gear'}];;
var compare = function (filter) {
return function (a,b) { //closure
var a = a[filter],
b = b[filter];
if (a < b) {
return -1;
}else if (a > b) {
return 1;
} else {
return 0;
}
};
};
filter = compare(filter); //set filter
console.log(data.sort(filter));
Looking at all the answers, I came up with my own solution that works cross-browser. The accepted solution does not work in IE or Safari. Also, the other solutions do not allow for sorting by descending.
/*! FUNCTION: ARRAY.KEYSORT(); **/
Array.prototype.keySort = function(key, desc){
this.sort(function(a, b) {
var result = desc ? (a[key] < b[key]) : (a[key] > b[key]);
return result ? 1 : -1;
});
return this;
}
var arr = [{name:'bob', artist:'rudy'}, {name:'johhny', artist:'drusko'}, {name:'tiff', artist:'needell'}, {name:'top', artist:'gear'}];
arr.keySort('artist');
arr.keySort('artist', true);