Compare properties of objects stored in array - javascript

I have an JavaScript array of this format:
var dimensions = [
{dim: 590, token:'...'},
{dim: 800, token:'.....'},
{dim: 2500, token:'........'}
];
Data in dimensions array is populated dynamically i.e. I' don't know whether it is gonna be zero, one or 50 objects with dim and token properties.
What I need is to pick token from object that have largest value of dim, and smallest value of dim. Anyone have a clue how do I do that?

I suggest to use Array.prototype.reduce(). It returns an object with the min and max object of the array.
var dimensions = [
{ dim: 590, token: '...' },
{ dim: 800, token: '.....' },
{ dim: 2500, token: '........' }
],
result = dimensions.reduce(function (r, a) {
r.min = r.min || a;
r.max = r.max || a;
return {
min: r.min.dim < a.dim ? r.min : a,
max: r.max.dim > a.dim ? r.max : a
};
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

var dimensions = [
{dim: 590, token:'...'},
{dim: 800, token:'.....'},
{dim: 2500, token:'........'}
];
// Sort the function in ascending order and pick the first and the last element.
var minmax = dimensions.sort(function(a,b){
return a.dim - b.dim;
}).filter(function(el, i){
return i==0 || i==dimensions.length-1;
});
// minmax will have two values, first will be minimum and second will be the largest.
console.log("Smallest: "+minmax[0].token);
console.log("Largest: "+minmax[1].token);

Use sort function to sort the array first and then pick first and last element from array.
dimensions.sort(function(a,b)
{
return a.dim - b.dim
});
var tokenOfSmallest = dimensions[0].token;
var tokenOfLargest = dimensions[dimensions.length - 1].token;

Although most answers provided here are correct, they are not the optimal solutions for your problem (Except Nina Scholz).
Sorting the array via Array.sort() takes a Big-O complexity of O(n*log(n)), but your problem could certainly be solved with a faster, O(n) algorithm.
var dimensions = [
{dim: 590, token:'...'},
{dim: 800, token:'.....'},
{dim: 2500, token:'........'}
];
function getTokenForLargestDim(array)
{
if(array.length === 0) { throw new Error('Empty array'); }
var maxDim = Number.NEGATIVE_INFINITY;
var maxIndex = -1;
for(var i=0; i < array.length; i++) {
var lastDim = maxDim;
if(dimensions[i].dim > maxDim) { maxDim = dimensions[i].dim; maxIndex = i;}
}
return array[maxIndex].token;
}
getTokenForLargestDim(dimensions);

sort the dimensions array first in ascending order and then get smallest and largest token value
dimensions.sort(function(a, b) {
return parseInt(a.dim) - parseInt(b.dim);
});
var smallest = dimensions[ 0 ].token;
var largest = dimensions[ dimensions.length - 1 ].token;

You can reduce your array to find max/min:
var result = dimensions.reduce(function(r, item) {
r.max = !r.max || r.max.dim < item.dim ? item : r.max;
r.min = !r.min || r.min.dim > item.dim ? item : r.min;
return r;
}, {});
console.log(result.max.token);
console.log(result.min.token);

Related

Getting elements from an array which adds up to a value greater than given value JavaScript

I recently just started to learn JavaScript to improve on my skills and a challenge came up which has thrown me off course.
I have a set of values of which I need to find elements in an array which would add up to give a number greater than the individual set of values. For instance
An array of [50, 100, 400, 800]
if I have a value say 50 it should return 50 as 50 is on the list. That is fine, but when If I have a value of say 421 it should return [400, 50] or if I have a value of 1751 it should return [800, 800, 100, 50, 50] as values that would add up to give the minimum value close to the given value.
I have tried the following:
I sorted the array values
Checked to see if the array includes a given value, if so return that value in a new array
If a value is less than min value and greater than the next value, return the next value
I tried looking into reduce method but I cant get my head around it and would like some expertise please.
The following is my code
const ItemSize = [50, 100, 400, 800]
function getItemSize(itemSize, n) {
const sortedItemSizes = ItemSize.sort(function compare(a, b) {
return a - b;
});
let item = [];
if (sortedItemSizes.includes(n)) {
item.push(n);
} else if (n > Math.min.apply(Math, sortedItemSizes) && n < sortedItemSizes[1]) {
item.push(sortedItemSizes[1]);
}
return item;
}
console.log(
getItemSize(ItemSize, 50)
)
You can:
Sort the array in descending values
Element by elment, check if it can be added to the solution, multiple times.
Return the resulting array
const arr = [50, 100, 800, 400];
function findSet(arr, sum) {
const sorted = arr.sort((a, b) => a < b ? 1 : -1);
const res = [];
sorted.forEach(n => {
while(n <= sum) {
sum -= n;
res.push(n);
}
});
return res;
}
console.log(findSet(arr, 51));
console.log(findSet(arr, 100));
console.log(findSet(arr, 150));
console.log(findSet(arr, 1751));

Javascript - Get max number from list of objects, each with a property that is a number

I have a list of objects, every object has the time property which is a integer (the code is actually typescript, so time is actually of type number).
I want to get the highest value of time amongst the objects in the list.
What is a concise, but understandable, way to do this? My current approach is the following, but it seems clunky:
let times = []
for(let adventurer of adventurers) {
times.push(adventurer.time)
}
Math.max(...times)
const maxVal = Math.max(...adventurers.map(o => o.time))
const maxTime = adventurers
.reduce((currentMax, { time }) => Math.max(currentMax, time), 0);
I'm not sure if this is valid syntax, but you could try:
function getMax(array) {
let max = 0;
for (let item in array) {
if (max < item ) max = item;
else continue;
}
return max;
}
Also, if you're dealing with time or another data model you may have to customize the sort.
This is javascript:
array.sort(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;
});
Try this
var adventurers = [
{time : 100},
{time : 120},
{time : 160},
{time : 90},
{time : 200},
]
const maxTime = adventurers.sort((val1,val2)=> {
return (val1.time < val2.time ) ? 1 : -1
})[0]
console.log(maxTime) // {time : 200}
console.log(maxTime.time) // 200

why is .sort return 0 reordering my results? [duplicate]

I'm looking to sort an array of about 200-300 objects, sorting on a specific key and a given order (asc/desc). The order of results must be consistent and stable.
What would be the best algorithm to use, and could you provide an example of it's implementation in javascript?
Thanks!
It is possible to get a stable sorting from a non-stable sort function.
Before sorting you get the position of all the elements.
In your sort condition, if both elements are equal, then you sort by the position.
Tada! You've got a stable sort.
I've written an article about it on my blog if you want to know more about this technique and how to implement it: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html
Since you are looking for something stable, the merge sort should do.
http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/
The code can be found at the above website:
function mergeSort(arr)
{
if (arr.length < 2)
return arr;
var middle = parseInt(arr.length / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle, arr.length);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right)
{
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
EDIT:
According to this post, it looks like Array.Sort in some implementations uses a merge sort.
Somewhat shorter version of the same thing using ES2017 features like arrow functions and destructuring:
Function
var stableSort = (arr, compare) => arr
.map((item, index) => ({item, index}))
.sort((a, b) => compare(a.item, b.item) || a.index - b.index)
.map(({item}) => item)
It accepts input array and compare function:
stableSort([5,6,3,2,1], (a, b) => a - b)
It also returns new array instead of making in-place sort like the built-in Array.sort() function.
Test
If we take the following input array, initially sorted by weight:
// sorted by weight
var input = [
{ height: 100, weight: 80 },
{ height: 90, weight: 90 },
{ height: 70, weight: 95 },
{ height: 100, weight: 100 },
{ height: 80, weight: 110 },
{ height: 110, weight: 115 },
{ height: 100, weight: 120 },
{ height: 70, weight: 125 },
{ height: 70, weight: 130 },
{ height: 100, weight: 135 },
{ height: 75, weight: 140 },
{ height: 70, weight: 140 }
]
Then sort it by height using stableSort:
stableSort(input, (a, b) => a.height - b.height)
Results in:
// Items with the same height are still sorted by weight
// which means they preserved their relative order.
var stable = [
{ height: 70, weight: 95 },
{ height: 70, weight: 125 },
{ height: 70, weight: 130 },
{ height: 70, weight: 140 },
{ height: 75, weight: 140 },
{ height: 80, weight: 110 },
{ height: 90, weight: 90 },
{ height: 100, weight: 80 },
{ height: 100, weight: 100 },
{ height: 100, weight: 120 },
{ height: 100, weight: 135 },
{ height: 110, weight: 115 }
]
However sorting the same input array using the built-in Array.sort() (in Chrome/NodeJS):
input.sort((a, b) => a.height - b.height)
Returns:
var unstable = [
{ height: 70, weight: 140 },
{ height: 70, weight: 95 },
{ height: 70, weight: 125 },
{ height: 70, weight: 130 },
{ height: 75, weight: 140 },
{ height: 80, weight: 110 },
{ height: 90, weight: 90 },
{ height: 100, weight: 100 },
{ height: 100, weight: 80 },
{ height: 100, weight: 135 },
{ height: 100, weight: 120 },
{ height: 110, weight: 115 }
]
Resources
Wikipedia
MDN
JSFiddle
Update
Array.prototype.sort is now stable in V8 v7.0 / Chrome 70!
Previously, V8 used an unstable QuickSort for arrays with more than 10 elements. Now, we use the stable TimSort algorithm.
source
I know that this question has been answered for some time, but I happen to have a good stable merge sort implementation for Array and jQuery in my clipboard, so I'll share it in the hopes that some future searchers might find it useful.
It allows you to specify your own comparison function just like the normal Array.sort implementation.
Implementation
// Add stable merge sort to Array and jQuery prototypes
// Note: We wrap it in a closure so it doesn't pollute the global
// namespace, but we don't put it in $(document).ready, since it's
// not dependent on the DOM
(function() {
// expose to Array and jQuery
Array.prototype.mergeSort = jQuery.fn.mergeSort = mergeSort;
function mergeSort(compare) {
var length = this.length,
middle = Math.floor(length / 2);
if (!compare) {
compare = function(left, right) {
if (left < right)
return -1;
if (left == right)
return 0;
else
return 1;
};
}
if (length < 2)
return this;
return merge(
this.slice(0, middle).mergeSort(compare),
this.slice(middle, length).mergeSort(compare),
compare
);
}
function merge(left, right, compare) {
var result = [];
while (left.length > 0 || right.length > 0) {
if (left.length > 0 && right.length > 0) {
if (compare(left[0], right[0]) <= 0) {
result.push(left[0]);
left = left.slice(1);
}
else {
result.push(right[0]);
right = right.slice(1);
}
}
else if (left.length > 0) {
result.push(left[0]);
left = left.slice(1);
}
else if (right.length > 0) {
result.push(right[0]);
right = right.slice(1);
}
}
return result;
}
})();
Example Usage
var sorted = [
'Finger',
'Sandwich',
'sandwich',
'5 pork rinds',
'a guy named Steve',
'some noodles',
'mops and brooms',
'Potato Chip Brand® chips'
].mergeSort(function(left, right) {
lval = left.toLowerCase();
rval = right.toLowerCase();
console.log(lval, rval);
if (lval < rval)
return -1;
else if (lval == rval)
return 0;
else
return 1;
});
sorted == ["5 pork rinds", "a guy named Steve", "Finger", "mops and brooms", "Potato Chip Brand® chips", "Sandwich", "sandwich", "some noodles"];
You can use the following function to perform a stable sort regardless of the native implementation, based on the assertion made in this answer.
Do note that as of ECMAScript 2019, the specification requires that the builtin sort() method perform a stable sort. With that in mind, an explicit stable sort function like the one below is still relevant if you are required to support older browsers that are not specification compliant.
// ECMAScript 5 implementation
function stableSort(array, compareFunction) {
'use strict';
var length = array.length;
var indices = new Uint32Array(length);
var i;
var slice;
// reference values by indices
for (i = 0; i < length; ++i) {
indices[i] = i;
}
// sort with fallback based on indices
indices.sort(function stableCompareFunction(compareFunction, a, b) {
var order = Number(compareFunction(this[a], this[b]));
return order || a - b;
}.bind(array, compareFunction));
slice = array.slice();
// re-order original array to stable sorted values
for (i = 0; i < length; ++i) {
array[i] = slice[indices[i]];
}
return array;
}
// usage
const array = Array(500000).fill().map(() => Number(Math.random().toFixed(4)));
const alwaysEqual = () => 0;
const isUnmoved = (value, index) => value === array[index];
// not guaranteed to be stable before ES2019
console.log(
'sort() stable?',
array.slice().sort(alwaysEqual).every(isUnmoved)
);
// guaranteed to be stable
console.log(
'stableSort() stable?',
stableSort(array.slice(), alwaysEqual).every(isUnmoved)
);
// performance using realistic scenario with unsorted big data
function time(arraySlice, algorithm, compare) {
var start;
var stop;
start = performance.now();
algorithm(arraySlice, compare);
stop = performance.now();
return stop - start;
}
const ascending = (a, b) => a - b;
const msSort = time(array.slice(), (array, compare) => array.sort(compare), ascending);
const msStableSort = time(array.slice(), (array, compare) => stableSort(array, compare), ascending);
console.log('sort()', msSort.toFixed(3), 'ms');
console.log('stableSort()', msStableSort.toFixed(3), 'ms');
console.log('sort() / stableSort()', (100 * msSort / msStableSort).toFixed(3) + '%');
Running the performance tests implemented above, stableSort() appears to run at about 72% of the speed of sort() on version 88 of Google Chrome and Microsoft Edge.
Using .bind() on the inline function within stableSort() used to boost relative performance significantly by avoiding unneeded scoped references on each call.
In practice, this no longer makes a difference since modern engines automatically perform this optimization now, but it is left in the implementation anyway in order to continue improving performance in older browsers which don't ship with this optimization.
The following sorts the supplied array, by applying the supplied compare function, returning the original index comparison when the compare function returns 0:
function stableSort(arr, compare) {
var original = arr.slice(0);
arr.sort(function(a, b){
var result = compare(a, b);
return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
});
return arr;
}
The example below sorts an array of names by surname, retaining the order of equal surnames:
var names = [
{ surname: "Williams", firstname: "Mary" },
{ surname: "Doe", firstname: "Mary" },
{ surname: "Johnson", firstname: "Alan" },
{ surname: "Doe", firstname: "John" },
{ surname: "White", firstname: "John" },
{ surname: "Doe", firstname: "Sam" }
]
function stableSort(arr, compare) {
var original = arr.slice(0);
arr.sort(function(a, b){
var result = compare(a, b);
return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
});
return arr;
}
stableSort(names, function(a, b) {
return a.surname > b.surname ? 1 : a.surname < b.surname ? -1 : 0;
})
names.forEach(function(name) {
console.log(name.surname + ', ' + name.firstname);
});
Here's a stable implementation. It works by using the native sort, but in cases where elements compare as equal, you break ties using the original index position.
function stableSort(arr, cmpFunc) {
//wrap the arr elements in wrapper objects, so we can associate them with their origional starting index position
var arrOfWrapper = arr.map(function(elem, idx){
return {elem: elem, idx: idx};
});
//sort the wrappers, breaking sorting ties by using their elements orig index position
arrOfWrapper.sort(function(wrapperA, wrapperB){
var cmpDiff = cmpFunc(wrapperA.elem, wrapperB.elem);
return cmpDiff === 0
? wrapperA.idx - wrapperB.idx
: cmpDiff;
});
//unwrap and return the elements
return arrOfWrapper.map(function(wrapper){
return wrapper.elem;
});
}
a non-thorough test
var res = stableSort([{a:1, b:4}, {a:1, b:5}], function(a, b){
return a.a - b.a;
});
console.log(res);
another answer alluded to this, but didn't post teh codez.
but, its not fast according to my benchmark. I modified a merge sort impl to accept a custom comparator function, and it was much faster.
You can also use Timsort. This is a really complicated algorithm (400+ lines, hence no source code here), so see Wikipedia's description or use one of the existing JavaScript implementations:
GPL 3 implementation. Packaged as Array.prototype.timsort. Appears to be an exact rewrite of Java code.
Public domain implementation Meant as a tutorial, the sample code only shows its use with integers.
Timsort is a highly optimized hybrid of mergesort and shuffle sort and is the default sorting algorithm in Python and in Java (1.7+). It is a complicated algorithm, since it uses different algorithms for many special cases. But as a result it's extremely fast under a wide variety of circumstances.
A simple one mergeSort from http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/
var a = [34, 203, 3, 746, 200, 984, 198, 764, 9];
function mergeSort(arr)
{
if (arr.length < 2)
return arr;
var middle = parseInt(arr.length / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle, arr.length);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right)
{
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
console.log(mergeSort(a));
I have to sort multidimensional arrays by an arbitrary column, and then by another. I use this function to sort:
function sortMDArrayByColumn(ary, sortColumn){
//Adds a sequential number to each row of the array
//This is the part that adds stability to the sort
for(var x=0; x<ary.length; x++){ary[x].index = x;}
ary.sort(function(a,b){
if(a[sortColumn]>b[sortColumn]){return 1;}
if(a[sortColumn]<b[sortColumn]){return -1;}
if(a.index>b.index){
return 1;
}
return -1;
});
}
Notice that ary.sort never returns zero, which is where some implementations of the "sort" function make decisions that might not be right.
This is pretty darn fast, too.
Here's how you could extend JS default Array object with a prototype method utilizing MERGE SORT. This method allows for sorting on a specific key (first parameter) and a given order ('asc'/'desc' as second parameter)
Array.prototype.mergeSort = function(sortKey, direction){
var unsortedArray = this;
if(unsortedArray.length < 2) return unsortedArray;
var middle = Math.floor(unsortedArray.length/2);
var leftSubArray = unsortedArray.slice(0,middle).mergeSort(sortKey, direction);
var rightSubArray = unsortedArray.slice(middle).mergeSort(sortKey, direction);
var sortedArray = merge(leftSubArray, rightSubArray);
return sortedArray;
function merge(left, right) {
var combined = [];
while(left.length>0 && right.length>0){
var leftValue = (sortKey ? left[0][sortKey] : left[0]);
var rightValue = (sortKey ? right[0][sortKey] : right[0]);
combined.push((direction === 'desc' ? leftValue > rightValue : leftValue < rightValue) ? left.shift() : right.shift())
}
return combined.concat(left.length ? left : right)
}
}
You can test this out yourself by dropping the above snippet into your browser console, then trying:
var x = [2,76,23,545,67,-9,12];
x.mergeSort(); //[-9, 2, 12, 23, 67, 76, 545]
x.mergeSort(undefined, 'desc'); //[545, 76, 67, 23, 12, 2, -9]
Or order based on a specific field in an array of objects:
var y = [
{startTime: 100, value: 'cat'},
{startTime: 5, value: 'dog'},
{startTime: 23, value: 'fish'},
{startTime: 288, value: 'pikachu'}
]
y.mergeSort('startTime');
y.mergeSort('startTime', 'desc');
So I needed a stable sort for my React+Redux app, and Vjeux's answer here helped me. However, my (generic) solution seems different than the others I see here so far, so I'm sharing it in case anyone else has a matching use-case:
I really just want to have something similar to the sort() API, where I can pass a comparator function.
Sometimes I can sort in-place, and sometimes my data is immutable (because Redux) and I need a sorted copy instead. So I need a stable sorting function for each use-case.
ES2015.
My solution is to create a typed array of indices, then use a comparison function to sort these indices based on the to-be-sorted array. Then we can use the sorted indices to either sort the original array or create a sorted copy in a single pass. If that's confusing, think of it this way: where you would normally pass a comparison function like:
(a, b) => {
/* some way to compare a and b, returning -1, 0, or 1 */
};
You now instead use:
(i, j) => {
let a = arrayToBeSorted[i], b = arrayToBeSorted[j];
/* some way to compare a and b, returning -1 or 1 */
return i - j; // fallback when a == b
}
Speed is good; it is basically the built-in sorting algorithm is, plus two linear passes in the end, and one extra layer of pointer indirection overhead.
Happy to receive feedback on this approach. Here is my full implementation of it it:
/**
* - `array`: array to be sorted
* - `comparator`: closure that expects indices `i` and `j`, and then
* compares `array[i]` to `array[j]` in some way. To force stability,
* end with `i - j` as the last "comparison".
*
* Example:
* ```
* let array = [{n: 1, s: "b"}, {n: 1, s: "a"}, {n:0, s: "a"}];
* const comparator = (i, j) => {
* const ni = array[i].n, nj = array[j].n;
* return ni < nj ? -1 :
* ni > nj ? 1 :
* i - j;
* };
* stableSortInPlace(array, comparator);
* // ==> [{n:0, s: "a"}, {n:1, s: "b"}, {n:1, s: "a"}]
* ```
*/
function stableSortInPlace(array, comparator) {
return sortFromIndices(array, findIndices(array, comparator));
}
function stableSortedCopy(array, comparator){
let indices = findIndices(array, comparator);
let sortedArray = [];
for (let i = 0; i < array.length; i++){
sortedArray.push(array[indices[i]]);
}
return sortedArray;
}
function findIndices(array, comparator){
// Assumes we don't have to worry about sorting more than
// 4 billion elements; if you know the upper bounds of your
// input you could replace it with a smaller typed array
let indices = new Uint32Array(array.length);
for (let i = 0; i < indices.length; i++) {
indices[i] = i;
}
// after sorting, `indices[i]` gives the index from where
// `array[i]` should take the value from, so to sort
// move the value at at `array[indices[i]]` to `array[i]`
return indices.sort(comparator);
}
// If I'm not mistaken this is O(2n) - each value is moved
// only once (not counting the vacancy temporaries), and
// we also walk through the whole array once more to check
// for each cycle.
function sortFromIndices(array, indices) {
// there might be multiple cycles, so we must
// walk through the whole array to check.
for (let k = 0; k < array.length; k++) {
// advance until we find a value in
// the "wrong" position
if (k !== indices[k]) {
// create vacancy to use "half-swaps" trick,
// props to Andrei Alexandrescu
let v0 = array[k];
let i = k;
let j = indices[k];
while (j !== k) {
// half-swap next value
array[i] = array[j];
// array[i] now contains the value it should have,
// so we update indices[i] to reflect this
indices[i] = i;
// go to next index
i = j;
j = indices[j];
}
// put original array[k] back in
// and update indices
array[i] = v0;
indices[i] = i;
}
}
return array;
}
I know this has been plenty answered. I just wanted to go ahead an post a quick TS implementation for anyone that landed here looking for that.
export function stableSort<T>( array: T[], compareFn: ( a: T, b: T ) => number ): T[] {
const indices = array.map( ( x: T, i: number ) => ( { element: x, index: i } ) );
return indices.sort( ( a, b ) => {
const order = compareFn( a.element, b.element );
return order === 0 ? a.index - b.index : order;
} ).map( x => x.element );
}
The method does no longer run in-place, as the native sort does. I also want to point out that it is not the most efficient. It adds two loops of the order O(n). though sort itself is most likely O(n log(n)) so it's less than that.
Some of the solutions mentioned are more performant, thought this might be less code, also using internal Array.prototype.sort.
(For a Javascript solution, just remove all the types)
According to the v8 dev blog and caniuse.com Array.sort is already stable as required by the spec in modern browsers, so you don't need to roll your own solution.
The only exception I can see is Edge, which should soon move over to chromium and support it as well.
function sort(data){
var result=[];
var array = data;
const array2=data;
const len=array2.length;
for(var i=0;i<=len-1;i++){
var min = Math.min.apply(Math,array)
result.push(min);
var index=array.indexOf(min)
array.splice(index,1);
}
return result;
}
sort([9,8,5,7,9,3,9,243,4,5,6,3,4,2,4,7,4,9,55,66,33,66]);
Counting Sort is faster than merge sort (it performs in O(n) time) and it is intended for use on integers.
Math.counting_sort = function (m) {
var i
var j
var k
var step
var start
var Output
var hash
k = m.length
Output = new Array ()
hash = new Array ()
// start at lowest possible value of m
start = 0
step = 1
// hash all values
i = 0
while ( i < k ) {
var _m = m[i]
hash [_m] = _m
i = i + 1
}
i = 0
j = start
// find all elements within x
while ( i < k ) {
while ( j != hash[j] ) {
j = j + step
}
Output [i] = j
i = i + 1
j = j + step
}
return Output
}
Example:
var uArray = new Array ()<br/>
var sArray = new Array ()<br/><br/>
uArray = [ 10,1,9,2,8,3,7,4,6,5 ]<br/>
sArray = Math.counting_sort ( uArray ) // returns a sorted array

Natural sort, array of objects, multiple columns, reverse, etc

I desperately need to implement client side sorting that emulates sorting through our tastypie api, which can take multiple fields and return sorted data. So if for example I have data like:
arr = [
{ name: 'Foo LLC', budget: 3500, number_of_reqs: 1040 },
{ name: '22nd Amendment', budget: 1500, number_of_reqs: 2000 },
{ name: 'STS 10', budget: 50000, number_of_reqs: 500 },
...
etc.
]
and given columns to sort e.g.:['name', '-number_of_reqs'] it should sort by name (ascending) and number_of_reqs (descending). I can't get my head around this,
first of all it has to be "natural sort", it supposed to be fairly easy to get if we're talking about sorting a single column, but I need to be able to sort in multiple.
Also I'm not sure why I'm getting different results (from the way how api does it) when using lodash's _.sortBy? Is _.sortBy not "natural" or it's our api broken?
Also I was looking for an elegant solution. Just recently started using Ramdajs, it's so freaking awesome. I bet it would be easier to build sorting I need using that? I've tried, still can't get it right. Little help?
upd:
I found this and using it with Ramda like this:
fn = R.compose(R.sort(naturalSort), R.pluck("name"))
fn(arr)
seems to work for flat array, yet I still need to find a way to apply it for multiple fields in my array
fn = R.compose(R.sort(naturalSort), R.pluck("name"))
seems to be working
Really? I would expect that to return a sorted array of names, not sort an array of objects by their name property.
Using sortBy unfortunately doesn't let us supply a custom comparison function (required for natural sort), and combining multiple columns in a single value that compares consistently might be possible but is cumbersome.
I still don't know how to do it for multiple fields
Functional programming can do a lot here, unfortunately Ramda isn't really equipped with useful functions for comparators (except R.comparator). We need three additional helpers:
on (like the one from Haskell), which takes an a -> b transformation and a b -> b -> Number comparator function to yield a comparator on two as. We can create it with Ramda like this:
var on = R.curry(function(map, cmp) {
return R.useWith(cmp, map, map);
return R.useWith(cmp, [map, map]); // since Ramda >0.18
});
or - just like ||, but on numbers not limited to booleans like R.or. This can be used to chain two comparators together, with the second only being invoked if the first yields 0 (equality). Alternatively, a library like thenBy could be used for this. But let's define it ourselves:
var or = R.curry(function(fst, snd, a, b) {
return fst(a, b) || snd(a, b);
});
negate - a function that inverses a comparison:
function negate(cmp) {
return R.compose(R.multiply(-1), cmp);
}
Now, equipped with these we only need our comparison functions (that natural sort is an adapted version of the one you found, see also Sort Array Elements (string with numbers), natural sort for more):
var NUMBER_GROUPS = /(-?\d*\.?\d+)/g;
function naturalCompare(a, b) {
var aa = String(a).split(NUMBER_GROUPS),
bb = String(b).split(NUMBER_GROUPS),
min = Math.min(aa.length, bb.length);
for (var i = 0; i < min; i++) {
var x = aa[i].toLowerCase(),
y = bb[i].toLowerCase();
if (x < y) return -1;
if (x > y) return 1;
i++;
if (i >= min) break;
var z = parseFloat(aa[i]) - parseFloat(bb[i]);
if (z != 0) return z;
}
return aa.length - bb.length;
}
function stringCompare(a, b) {
a = String(a); b = String(b);
return +(a>b)||-(a<b);
}
function numberCompare(a, b) {
return a-b;
}
And now we can compose exactly the comparison on objects that you want:
fn = R.sort(or(on(R.prop("name"), naturalCompare),
on(R.prop("number_of_reqs"), negate(numberCompare))));
fn(arr)
I think this works.
var arr = [
{ name: 'Foo LLC', budget: 3500, number_of_reqs: 1040 },
{ name: '22nd Amendment', budget: 1500, number_of_reqs: 2000 },
{ name: 'STS 10', budget: 50000, number_of_reqs: 5000 },
{ name: 'STS 10', budget: 50000, number_of_reqs: 500 }
];
var columns = ['name', 'number_of_reqs'];
var NUMBER_GROUPS = /(-?\d*\.?\d+)/g;
var naturalSort = function (a, b, columnname) {
var a_field1 = a[columnname],
b_field1 = b[columnname],
aa = String(a_field1).split(NUMBER_GROUPS),
bb = String(b_field1).split(NUMBER_GROUPS),
min = Math.min(aa.length, bb.length);
for (var i = 0; i < min; i++) {
var x = parseFloat(aa[i]) || aa[i].toLowerCase(),
y = parseFloat(bb[i]) || bb[i].toLowerCase();
if (x < y) return -1;
else if (x > y) return 1;
}
return 0;
};
arr.sort(function(a, b) {
var result;
for (var i = 0; i < columns.length; i++) {
result = naturalSort(a, b, columns[i]);
if (result !== 0) return result; // or -result for decending
}
return 0; //If both are exactly the same
});
console.log(arr);
Bergi's answer is useful and quite interesting, but it changes the API you requested. Here's one that creates the API you were seeking:
var multisort = (function() {
var propLt = R.curry(function(name, a, b) {
return a[name] < b[name];
});
return function(keys, objs) {
if (arguments.length === 0) {throw new TypeError('cannot sort on nothing');}
var fns = R.map(function(key) {
return key.charAt(0) === "-" ?
R.pipe(R.comparator(propLt(R.substringFrom(1, key))), R.multiply(-1)) :
R.comparator(propLt(key));
}, keys);
var sorter = function(a, b) {
return R.reduce(function(acc, fn) {return acc || fn(a, b);}, 0, fns);
}
return arguments.length === 1 ? R.sort(sorter) : R.sort(sorter, objs);
};
}());
multisort(['name', '-number_of_reqs'], arr); //=> sorted clone
It's manually curried rather than calling R.curry because a fair bit of the work is involved in creating the separate sort functions, which could then be reused if you are sorting many lists with the same set of keys. If that's not a concern, this could be simplified a bit.
If you're willing to add another dependency to your project, #panosoft/ramda-utils comes with a compareProps function that does exactly what the original question was asking for.
So, given your original example, to sort descending by budget and then by name, you could do something like this:
var props = ["-budget", "name"];
var comparator = Ru.compareProps(props);
var sortedList = R.sort(comparator, arr);
use the javascript native sort:
Array.prototype.multisort = function(columns) {
var arr = this;
arr.sort(function(a, b) {
return compare(a, b, 0);
});
function compare(a, b, colindex) {
if (colindex >= columns.length) return 0;
var columnname = columns[colindex];
var a_field1 = a[columnname];
var b_field1 = b[columnname];
var asc = (colindex % 2 === 0);
if (a_field1 < b_field1) return asc ? -1 : 1;
else if (a_field1 > b_field1) return asc ? 1 : -1;
else return compare(a, b, colindex + 1);
}
}
var arr = [{ name: 'Foo LLC', budget: 3500, number_of_reqs: 1040 },
{ name: '22nd Amendment',budget: 1500, number_of_reqs: 2000 },
{ name: 'STS 10', budget: 50000,number_of_reqs: 5000 },
{ name: 'STS 10', budget: 50000,number_of_reqs: 500 }];
arr.multisort(['name', 'number_of_reqs']);
if (window.console) window.console.log(arr);

Need Improvisation with this Unique Array Function

I'm using following code to detect Unique Numbers within Array.
// Array
var sortBp = [ 700, 500, 500, 501, 600];
// Find Unique Numbers
Array.prototype.unique = function(){
var bpAllArrays = this;
var uniqueArrays = [];
for(var i=bpAllArrays.length;i--;){
var uniqueArray = bpAllArrays[i];
if($.inArray( uniqueArray, uniqueArrays )===-1){
uniqueArrays.unshift(uniqueArray);
}
}
return uniqueArrays;
}
// Append Only Unique Numbers
$.each(sortBp.unique(), function(index, value) {
$("body").append(value);
});
This code works fine. Here is working Fiddle : http://jsfiddle.net/ScPbe/
but I want to add another function to detect :
If Very Next Number is present in Array, Remove Higher number & Only append lower Number.
Example : [ 500, 501 ] In this case, I only want 500 to be consider as unique number & not 501
So Final result should look something like :
[ 700, 500, 600]
How can I achieve this ?
Assuming the double values come one after the other (cannot appear anywhere in the array), this solution will be much simpler:
var sortBp = [ 700, 500, 500, 501, 600, 900, 900, 901];
for (var cur, last, i=0; i<sortBp.length; i++) {
cur = sortBp[i];
if (cur === last || cur === last + 1)
continue;
$('body').append(" "+cur);
last = cur;
}
With jQuery.each:
var last;
$.each(sortBp, function(i, cur) {
if (cur === last || cur === last + 1)
return;
$('body').append(" "+cur);
last = cur;
});
Elegant solution using es5's .reduce (might need to be shimmed):
var display = [];
sortBp.reduce(function(last, cur) {
if (cur === last || cur === last+1)
return last;
display.push(cur);
return cur;
}, undefined);
$('body').append(display.join(" "));
Finally, I have comeup with following code. It checks if current value in array is greater than prev value by 1
Here is fiddle : http://jsfiddle.net/ScPbe/5/
// Array
var sortBp = [ 700, 500, 500, 501, 600, 601, 610,612];
// Find Unique Numbers
Array.prototype.unique = function(){
var bpAllArrays = this;
var uniqueArrays = [];
for(var i=bpAllArrays.length;i--;){
var uniqueArray = bpAllArrays[i];
if($.inArray( uniqueArray, uniqueArrays )===-1){
uniqueArrays.unshift(uniqueArray);
}
}
return uniqueArrays;
}
// Append Only Unique Numbers
var arr = sortBp.unique();
$.each(arr, function(index, value) {
// Chect if Current Value > Prev by 1
var prev = arr[index - 1];
if (value - prev !== 1 ) {
$('body').append(" "+value);
}
});

Categories