Complex sort of array of objects - javascript

I have a variable and array of objects e.g:
var selectedName = 'fff';
[{
percentage: Math.round(percentage),
count: count,
name: 'bbb',
index: 11,
},
{
percentage: Math.round(percentage),
count: 200,
name: 'aaa',
index: 2,
},
{
percentage: Math.round(percentage),
count: 400,
name: 'All',
index: 7,
},
{
percentage: Math.round(percentage),
count: count,
name: 'fff',
index: 8,
},
{
percentage: Math.round(percentage),
count: count,
name: 'ccc',
index: 3,
}],
I want to sort these as follows: the object which has the name 'All' should always be first. The next object should be the one with the name that matches selectedName, in this case 'fff'. And then the rest of the objects should be ordered by ascending order of their 'index' property.
Is this possible in one Array.sort() method?

Yes, here an example:
var selectedName = 'fff';
let percentage = 1;
let count = 2;
var data=[{percentage:Math.round(percentage),count:count,name:"bbb",index:11},{percentage:Math.round(percentage),count:200,name:"aaa",index:2},{percentage:Math.round(percentage),count:400,name:"All",index:7},{percentage:Math.round(percentage),count:count,name:"fff",index:8},{percentage:Math.round(percentage),count:count,name:"ccc",index:3}];
arr.sort((i, j) => {
if (i.name === j.name && (i.name === 'All' || i.name === selectedName)) return 0;
if (i.name === 'All') return -1;
if (j.name === 'All') return 1;
if (i.name === selectedName) return -1;
if (j.name === selectedName) return 1;
if (i.index < j.index) return -1;
if (i.index > j.index) return 1;
return 0;
})
console.log(arr)

Sure you can do it like so:
var selectedName = 'fff';
var percentage = 23;
var count = 3;
var data=[{percentage:Math.round(percentage),count:count,name:"bbb",index:11},{percentage:Math.round(percentage),count:200,name:"aaa",index:2},{percentage:Math.round(percentage),count:400,name:"All",index:7},{percentage:Math.round(percentage),count:count,name:"fff",index:8},{percentage:Math.round(percentage),count:count,name:"ccc",index:3}];
function sortItems(a, b) {
if (a.name === 'All') {
return -1
}
if (b.name === 'All') {
return 1;
}
if (a.name === selectedName) {
return -1
}
if (b.name === selectedName) {
return 1
}
return a.index - b.index;
}
console.log(items.sort(sortItems));

You can try something like this:
Idea
(a.name !== priorityName) will yield a boolean value. When you use -(minus) operator on them, it is converted to numeric value(true: 1, false: 0).
So if both are neither of priority, both will yield 1 and output will be 0, which is falsey in JS.
Failure of previous expression will call current expression and would loop till last expression
var selectedName = 'fff';
var priorityName = "All";
var percentage = 50.4, count= 0;
var data=[{percentage:Math.round(percentage),count:count,name:"bbb",index:11},{percentage:Math.round(percentage),count:200,name:"aaa",index:2},{percentage:Math.round(percentage),count:400,name:"All",index:7},{percentage:Math.round(percentage),count:count,name:"fff",index:8},{percentage:Math.round(percentage),count:count,name:"ccc",index:3}];
data.sort(function(a,b){
return (a.name !== priorityName) - (b.name !== priorityName) ||
(a.name !== selectedName) - (b.name !== selectedName) ||
a.index - b.index;
})
console.log(data)

You can use a helping object holding the priority of each element. In your particular case, the highest priority has All value.
So firstly we sort the values with higher priority (All, 'fff') and when it's done, we sort the rest by the index value.
Note: Since we are sorting it by ascending order the priority values have to be negative. If we would sort it by a descending order (b - a), they would be positive.
var arr = [{percentage:Math.round("percentage"),count:"count",name:"bbb",index:11},{percentage:Math.round("percentage"),count:200,name:"aaa",index:2},{percentage:Math.round("percentage"),count:400,name:"All",index:7},{percentage:Math.round("percentage"),count:"count",name:"fff",index:8},{percentage:Math.round("percentage"),count:"count",name:"ccc",index:3}],
selectedName = 'fff',
result = arr.sort(function(a,b){
var order = {All: -2, [selectedName]: -1, default: 0};
return (order[a.name] || order.default) - (order[b.name] || order.default) || a.index - b.index;
});
console.log(result);

Related

how to sort array of objects based on id and name

I want to sort Below array based on name,receiver_id,sender_id and role_id
this.role_id = 3
this.Data = [
{receiver_id: 3,sender_id:4,name: 'john',},
{receiver_id: 4,sender_id:3,name: 'james'},
{receiver_id: 2,sender_id:3,name: 'jane'},
{receiver_id: null,sender_id:null,name: 'charles'},
{receiver_id: null,sender_id:null,name: 'aaron'},
{receiver_id: 2,sender_id:4,name: 'alex'},
{receiver_id: 3,sender_id:2,name: 'david'},
];
I want array objects with receiver_id == role_id or sender_id == role_id to be on top of array and also it should be in alphabetical order. like this
this.Data = [
{receiver_id: 3,sender_id:2,name: 'david'},
{receiver_id: 4,sender_id:3,name: 'james'},
{receiver_id: 2,sender_id:3,name: 'jane'},
{receiver_id: 3,sender_id:4,name: 'john'},
{receiver_id: null,sender_id:null,name: 'aaron'},
{receiver_id: 2,sender_id:4,name: 'alex'},
{receiver_id: null,sender_id:null,name: 'charles'},
];
as of now I can sort based on name only
let colName = 'name'
this.Data.sort((b, a) => a[colName] < b[colName] ? 1 : a[colName] > b[colName] ? -1 : 0)
how to do this?
It might help to factor the conditions into functions. vip means the object satisfies the numerical test. The other key idea is that vip sameness defaults to the alpha sort. This way we get the alpha sort amongst both vips and non-vips.
let role_id = 3
let data = [
{receiver_id: 3,sender_id:4,name: 'john',},
{receiver_id: 4,sender_id:3,name: 'james'},
{receiver_id: 2,sender_id:3,name: 'jane'},
{receiver_id: 2,sender_id:4,name: 'alex'},
{receiver_id: 3,sender_id:2,name: 'david'},
];
function diff(a, b) {
const vip = obj => (role_id === obj.receiver_id) || (role_id === obj.sender_id)
const vipA = vip(a), vipB = vip(b)
return vipA === vipB ? a.name.localeCompare(b.name) : (vipA ? -1 : 1)
}
console.log(data.sort(diff))
With the sort method you determine if an item A goes before or after the other (B). First you need to define priorities, based on your description I guess:
1º: items where receiver_id == role_id || sender_id == role_id, ordered alphabetically
2º: items where receiver_id != role_id && sender_id != role_id, ordered alphabetically
That would be:
this.Data.sort( (a, b) => {
let conditionForA = a.receiver_id == role_id || a.sender_id == role_id;
let conditionForB = b.receiver_id == role_id || b.sender_id == role_id;
if (conditionForA && !conditionForB) {
// A > B
return 1;
}
if (conditionForB && !conditionForA) {
// B > A
return -1;
}
// For now A = B, let's compare alphabetically
if (a.name > b.name) return 1;
else if (a.name < b.name) return -1;
return 0; //equal
});
You could try something like this:
this.Data.filter(a => (a.receiver_id === 3 || a.sender_id === 3))
.sort((b, a) => a.name < b.name ? 1 : a.name > b.name ? -1 : 0)
.concat(
this.Data.filter(a => !(a.receiver_id === 3 || a.sender_id === 3))
.sort((b, a) => a.name < b.name ? 1 : a.name > b.name ? -1 : 0))
First, we filter only those entries which satisfy the condition receiver_id == role_id || sender_id == role_id, then we sort them by name.
We do the same for those entries which do not satisfy the condition.
And finally we concatenate the two resulting arrays.
You can first sort them after thier "sender_id" and then iterrate over them and sort Alphabetically. Note that the code below is not perfect an can probably writtern more efficent.
Example:
function sort(array) {
// returns function if array is undefined or empty
if(!array) return;
let finalList = [];
let swapped = false;
do {
swapped = false;
for(let i = array.length - 1; i > 0; i--) {
if(array[i].sender_id < array[i - 1].sender_id) {
let temp = array[i];
array[i] = array[i - 1];
array[i - 1] = temp;
swapped = true;
}
}
} while (swapped);
// sorts alphabetic
let lastBucket = [];
let lastId = 1;
for(entry in array) {
if(array[entry].sender_id > lastId) {
lastId = array[entry].sender_id;
let swapped = false;
do {
swapped = false;
for(let i = lastBucket.length - 1; i > 0; i--) {
if(lastBucket[i].name < lastBucket[i - 1].name) {
let temp = lastBucket[i];
lastBucket[i] = lastBucket[i - 1];
lastBucket[i - 1] = temp;
swapped = true;
}
}
for (item in lastBucket) {
finalList.push(lastBucket[item]);
}
lastBucket = [];
} while(swapped);
}
lastBucket.push(array[entry]);
}
for(item in lastBucket) {
finalList.push(lastBucket[item]);
}
return finalList;
}
// Test data
const sampleArray = [
{
sender_id: 1,
name: "test"
},
{
sender_id: 3,
name: "abc"
},
{
sender_id: 2,
name: "acd"
},
{
sender_id: 1,
name: "qqqqq"
},
{
sender_id: 9,
name: "hello world"
},
{
sender_id: 1,
name: "abc"
}
]
let sorted = sort(sampleArray);

d3 js sort error in chrome [duplicate]

I have an array of objects to sort. Each object has two parameters: Strength and Name
objects = []
object[0] = {strength: 3, name: "Leo"}
object[1] = {strength: 3, name: "Mike"}
I want to sort first by Strength and then by name alphabetically. I am using the following code to sort by the first parameter. How do I sort then by the second?
function sortF(ob1,ob2) {
if (ob1.strength > ob2.strength) {return 1}
else if (ob1.strength < ob2.strength){return -1}
return 0;
};
Thanks for your help.
(I am using Array.sort() with the aforementioned sortF as the sort comparison function passed into it.)
Expand your sort function to be like this;
function sortF(ob1,ob2) {
if (ob1.strength > ob2.strength) {
return 1;
} else if (ob1.strength < ob2.strength) {
return -1;
}
// Else go to the 2nd item
if (ob1.name < ob2.name) {
return -1;
} else if (ob1.name > ob2.name) {
return 1
} else { // nothing to split them
return 0;
}
}
A < and > comparison on strings is an alphabetic comparison.
This little function is often handy when sorting by multiple keys:
cmp = function(a, b) {
if (a > b) return +1;
if (a < b) return -1;
return 0;
}
or, more concisely,
cmp = (a, b) => (a > b) - (a < b)
Which works because in javascript:
true - true // gives 0
false - false // gives 0
true - false // gives 1
false - true // gives -1
Apply it like this:
array.sort(function(a, b) {
return cmp(a.strength,b.strength) || cmp(a.name,b.name)
})
Javascript is really missing Ruby's spaceship operator, which makes such comparisons extremely elegant.
You could chain the sort order with logical OR.
objects.sort(function (a, b) {
return a.strength - b.strength || a.name.localeCompare(b.name);
});
When I was looking for an answer to this very question, the answers I found on StackOverflow weren't really what I hoped for. So I created a simple, reusable function that does exactly this. It allows you to use the standard Array.sort, but with firstBy().thenBy().thenBy() style.
https://github.com/Teun/thenBy.js
PS. This is the second time I post this. The first time was removed by a moderator saying "Please don't make promotional posts for your own work". I'm not sure what the rules are here, but I was trying to answer this question. I'm very sorry that it is my own work. Feel free to remove again, but please point me to the rule involved then.
steve's answer, but prettier.
objects.sort(function(a,b)
{
if(a.strength > b.strength) {return 1;}
if(a.strength < b.strength) {return -1;}
if(a.name > b.name ) {return 1;}
if(a.name < b.name ) {return -1;}
return 0;
}
function sortF(ob1,ob2) {
if (ob1.strength > ob2.strength) {return 1}
else if (ob1.strength < ob2.strength) {return -1}
else if (ob1.name > ob2.name) {return 1}
return -1;
};
EDIT: Sort by strength, then if strength is equal, sort by name.
The case where strength and name are equal in both objects doesn't need to be accounted for seperately, since the final return of -1 indicates a less-than-or-equal-to relationship. The outcome of the sort will be correct. It might make it run faster or slower, I don't know. If you want to be explicit, just replace
return -1;
with
else if (ob1.name < ob2.name) {return -1}
return 0;
Find 'sortFn' function below. This function sorts by unlimited number of parameters(such as in c#: SortBy(...).ThenBy(...).ThenByDesc(...)).
function sortFn() {
var sortByProps = Array.prototype.slice.call(arguments),
cmpFn = function(left, right, sortOrder) {
var sortMultiplier = sortOrder === "asc" ? 1 : -1;
if (left > right) {
return +1 * sortMultiplier;
}
if (left < right) {
return -1 * sortMultiplier;
}
return 0;
};
return function(sortLeft, sortRight) {
// get value from object by complex key
var getValueByStr = function(obj, path) {
var i, len;
//prepare keys
path = path.replace('[', '.');
path = path.replace(']', '');
path = path.split('.');
len = path.length;
for (i = 0; i < len; i++) {
if (!obj || typeof obj !== 'object') {
return obj;
}
obj = obj[path[i]];
}
return obj;
};
return sortByProps.map(function(property) {
return cmpFn(getValueByStr(sortLeft, property.prop), getValueByStr(sortRight, property.prop), property.sortOrder);
}).reduceRight(function(left, right) {
return right || left;
});
};
}
var arr = [{
name: 'marry',
LocalizedData: {
'en-US': {
Value: 10000
}
}
}, {
name: 'larry',
LocalizedData: {
'en-US': {
Value: 2
}
}
}, {
name: 'marry',
LocalizedData: {
'en-US': {
Value: 100
}
}
}, {
name: 'larry',
LocalizedData: {
'en-US': {
Value: 1
}
}
}];
document.getElementsByTagName('pre')[0].innerText = JSON.stringify(arr)
arr.sort(sortFn({
prop: "name",
sortOrder: "asc"
}, {
prop: "LocalizedData[en-US].Value",
sortOrder: "desc"
}));
document.getElementsByTagName('pre')[1].innerText = JSON.stringify(arr)
pre {
font-family: "Courier New" Courier monospace;
white-space: pre-wrap;
}
Before:
<pre></pre>
Result:
<pre></pre>
With ES6 you can do
array.sort(function(a, b) {
return SortFn(a.strength,b.strength) || SortFn(a.name,b.name)
})
private sortFn(a, b): number {
return a === b ? 0 : a < b ? -1 : 1;
}
Here is the function I use. It will do an arbitrary number.
function Sorter(){
var self = this;
this.sortDefs = [];
for (let i = 0; i < arguments.length; i++) {
// Runs 5 times, with values of step 0 through 4.
this.sortDefs.push(arguments[i]);
}
this.sort = function(a, b){
for (let i = 0; i < self.sortDefs.length; i++) {
if (a[self.sortDefs[i]] < b[self.sortDefs[i]]) {
return -1;
} else if (a[self.sortDefs[i]] > b[self.sortDefs[i]]) {
return 1
}
}
return 0;
}
}
data.sort(new Sorter('category','name').sort);
In 2018 you can use just sort() ES6 function, that do exactly, what you want.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Sorting an array - passing a compare function

I have the following code:
var compare = function( nodeA, nodeB ){
return +nodeA.index - +nodeB.index;
};
var sort = function( nodes ){
nodes.sort( compare );
};
The node has this (pseudo) structure:
{
index: <integer>
value: <literal>
}
And it currently sorts them the regular way, when I call the sort function, and print out the index's of each node:
0
1
2
3
How can I change my current logic to make it look like this? :
1
2
3
0 <-- 0 should be considered the biggest index
You can add special handling for zeroes:
var compare = function(nodeA, nodeB) {
// in case both sides are equal
if (nodeA.index === nodeB.index) {
return 0;
}
if (nodeA.index === 0) {
return 1;
}
if (nodeB.index === 0) {
return -1;
}
return +nodeA.index - +nodeB.index;
};
var data = [{
index: 2,
value: 'a'
}, {
index: 0,
value: 'b'
}, {
index: 3,
value: 'c'
}, {
index: 1,
value: 'd'
}]
data.sort(compare);
console.log(data);
You can first sort by condition that index != 0 and then just sort by index value.
var data = [{
index: 2,
value: 'a'
}, {
index: 0,
value: 'b'
},{
index: 3,
value: 'c'
},{
index: 1,
value: 'd'
}]
var result = data.sort(function(a, b) {
return (b.index != 0) - (a.index != 0) || (a.index - b.index)
})
console.log(result)
You just need to change your compare function a little bit:
var compare = function( nodeA, nodeB ){
if (!+nodeA.index) return 1;
if (!+nodeB.index) return -1;
return +nodeA.index - +nodeB.index;
};
Just change compare to:
var compare = function( nodeA, nodeB ){
return ((+nodeA.index || Infinity) - (+nodeB.index || Infinity)) || Infinity;
};
|| operator returns the first value that is not "falsy", which is a "truthy" value, but is also the actual value. This is a EMCA5 "trick" to create default values to variables.
So explaining:
for nodeA.index == 0 && nodeB.index > 0 => Infinity - somevalue == Infinity
for nodeA.index > 0 && nodeB.index == 0 => somevalue - Infinity == -Infinity
for nodeA.index == 0 && nodeB.index == 0 => Infinity - Infinity == NaN in which case the || Infinity option is choosen

Traverse digits in strings

I'm trying to consider a function that takes in a flat array of string decimal integers that represent nodes in a tree, each period connotes hierarchy in that tree. I'm trying to create prevNode and nextNode functions. that take three parameters ids, id, planeLock. If a node has no prev or next id then false is returned. If planeLock is true then instead of going to the next node in the tree (e.g. from 1 to 0.1) it will go to the next node in that plane (eg. from 1 to 0) otherwise know as it's sibling, and not it's siblings deepest child.
var ids = [
'0',
'0.1',
'1',
'2',
'2.0',
'2.1',
]
prevNode(ids, '0') -> false // no prev node
prevNode(ids, '1', true) -> 0 // pass true stays on same plane
prevNode(ids, '1') -> 0.1 // prev node in tree
prevNode(ids, '2.0', true) -> false
prevNode(ids, '2.0') -> 2 // goes up one node
How can I parse these strings to get the desired results?
One possible approach:
function getLevel(id) {
return id.split('.').length;
}
function siblingNode(ids, id, planeLock, goesBack) {
var index = ids.indexOf(id);
var level = getLevel(id);
while (goesBack ? --index >= 0 : ++index < ids.length) {
var currEl = ids[index];
var currLevel = getLevel(currEl);
if (!planeLock || currLevel === level) {
return currEl;
}
if (currLevel < level) {
break;
}
}
return false;
}
function prevNode(ids, id, planeLock) {
return siblingNode(ids, id, planeLock, true);
}
function nextNode(ids, id, planeLock) {
return siblingNode(ids, id, planeLock, false);
}
Demo. Apparently, there's a tradeoff between memoizing all the levels (speedy but costs memory) and not (vice versa). If the source array is dynamic, and you'll have to look for a place when inserting new items, I'd strongly recommend memoizing approach (as you'll have to check level at each insertion).
Sorting the whole thing is a good approach. But if you want to extend this with additional functionality it may be better to convert your id list into a tree.
function createSortedTree(ids) {
var tree = {name: "", id: "root", children: {}};
function insert(tree, elem) {
if(!tree.children[elem[0]]) {
tree.children[elem[0]] = {
id: elem[0],
children: {},
parent: tree,
name: tree.id === "root" ? "" + elem[0] : tree.name + "." + elem[0]
};
}
if(elem.length > 1) insert(tree.children[elem[0]], elem.slice(1));
}
for(i in ids) insert(tree, ids[i].split("."));
function traverse(tree) {
if(current) {
current.next = tree;
tree.prev = current;
}
current = tree;
var children = Object.keys(tree.children)
.sort(function(a, b) {if(a < b) return -1; else if(a > b) return 1; else return 0;})
.map(function(key) {return tree.children[key]});
for(i in children) {
if(i > 0) children[i].prevPlane = children[i-1];
if(i < children.length - 1) children[i].nextPlane = children[i+1];
traverse(children[i]);
}
}
var current = null;
traverse(tree);
return tree;
}
function getNode(tree, id) {
if(typeof id === "string") id = id.split(".");
if(id.length === 0) return tree;
else return getNode(tree.children[id[0]], id.slice(1));
}
var tree = createSortedTree(["0", "0.1", "1", "2", "2.0", "2.1"])
var node = getNode(tree, "2.0");
console.log(node.prev.name);
console.log(node.next.name);
var node = getNode(tree, "1");
console.log(node.prev.name);
console.log(node.prevPlane.name);
http://jsfiddle.net/jxyqjq3c/
var _ = require('lodash')
function compare (n1, n2) {
var path1 = n1.split('.')
var path2 = n2.split('.')
var maxLen = Math.max(path1.length, path2.length)
var i = 0
while (i < maxLen) {
if (!path1[i] || +path1[i] < +path2[i]) {
return -1
}
if (!path2[i] || +path1[i] > +path2[i]) {
return 1
}
i++
}
return 0
}
function subset (ids, id) {
return _.filter(ids, function (_id) {
var _idArr = _id.split('.')
var idArr = id.split('.')
var _idChop = _.take(_idArr, _idArr.length - 1).join('.')
var idChop = _.take(idArr, idArr.length - 1).join('.')
if (_idChop === idChop) return true
return false
})
}
function metaInfo (ids, id) {
ids = ids.sort(compare)
var idIndex = ids.indexOf(id)
var meta = {}
meta.prev = (ids[idIndex - 1]) ? ids[idIndex - 1] : false
meta.next = (ids[idIndex + 1]) ? ids[idIndex + 1] : false
var idsSubset = subset(ids, id)
var idSubsetIndex = idsSubset.indexOf(id)
meta.prevSibling = (idsSubset[idSubsetIndex - 1]) ? idsSubset[idSubsetIndex - 1] : false
meta.nextSibling = (idsSubset[idSubsetIndex + 1]) ? idsSubset[idSubsetIndex + 1] : false
return meta
}
var ids = [ '0', '1', '2', '3', '0.0.0', '0.0.1', '0.0', '1.0' ]
var val = metaInfo(ids, '1')
console.log(val)
Here's a possibility. The implementation for nextNode would follow the same approach and re-use most of the function with the exception of changing the way the iterator is behaving.
function prevNode(collection, item, planeLock) {
var iterator = collection.indexOf(item) - 1
if (planeLock) {
while( ~iterator
&& !( item.split('.').length === 1 && collection[iterator].split('.').length === 1)
&& !( item.split('.').length === collection[iterator].split('.').length && item.split('.')[0] === collection[iterator].split('.')[0] ) )
iterator--
return ~iterator ? collection[iterator] : false
} else return collection[iterator] || false
}

Sort array on key value

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);

Categories

Resources