Recursive sort in JS - javascript

I was asked in an interview to write a program/algo to sort an array of number using recursion.
Though I vaguely answered it, I tried and came up with following code:
You can use following JSFiddle link to play around.
function sort(arr) {
if (arr.length === 2) {
const v1 = arr[0];
const v2 = arr[1];
const isGreater = (
(isString(v1) && isString(v2) && v1.toString().toLocaleCompare(v2) > 0) ||
(isNumber(v1) && isNumber(v2) && v1 > v2)
);
return isGreater ? [ v2, v1 ] : [ v1, v2 ];
} else {
const last = arr.pop();
const ret = sort(arr);
const newLast = ret.peekLast();
if (newLast < last) {
return [ ...ret, last ];
} else {
return sort( [ last, ...ret ] );
}
}
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return Number.isFinite(value); }
Array.prototype.peekLast = function () { return this.slice().pop(); }
//console.log(sort([1,2,3,4,5]))
console.log(sort([5,4,3,2,1]))
The algo I implemented is:
Take the array and check if its length is greater than 2.
If yes,
Remove last element and store it in a variable.
Again call same function without last element till it has 2 items.
Accept array returned from recursive call and peek the last element.
If newLast value is greater than previousLast
Push previousLast as first element and again call itself with this array.
If not, push previousLast to array and return it.
Else,
For number and string check equality and return correct order.
For anything else, return same value
Question is, is there a better way to implement (algo wise)?
Note: I'm not expecting code improvements. Objective of this question is improvement in algo part or any general stuff I have missed.
I also know, current code does not support:
Sort order. It will sort ascending only.
May break for Date objects, and does not support Objects in general.
Thanks!

I think most interviewers would expect you to respond with quicksort or merge sort (or both) given that question. Of the two, quicksort, is easier to remember and recreate in a pinch because the merge step of merge sort is easy to mess up.
Quicksort is a really beautiful algorithm and is a natural fit for javascript's functional tools. It is worth really understanding if you'll be doing interviews:
const arr = [6, 1, 5, 3, 9, 6, 7, 10, 16, 4, 0, 12, 2]
function qsort(arr){
if (arr.length < 2) return arr
// choose a pivot, p
// the choice of pivot can effect worst-case performance
// for this, we'll just use the first element.
const [p, ...rest] = arr
// partition array into element greater and lesser that the pivot
// this can be optimized so you don't loop through the array twice
const low = rest.filter(n => n <= p)
const high = rest.filter(n => n > p)
// recurse on both partitions and reassemble as recursion unwinds
return [...qsort(low), p, ...qsort(high)]
}
console.log(qsort(arr).join(', '))

I see a vein of intermediate value creation that is not inconsequential.
peekLast calls Array.prototype.slice which makes a copy of the array. You copy an entire array just to return the last element.
Array.prototype.peekLast = function () { return this.slice().pop(); }
Array.prototype.peekLast = function () { return this[this.length]; }
This gives you the same result every time without the need to copy.
Use of spread arguments in expressions like [ ...arr, x ] copies arr entirely.
arr.concat([ x ]) does the same thing without making copy (or mutation) of arr
You call peekLast and use ...x once per element in the input. Calling sort on a list of just 100 items will copy over 10,000 elements, for these operations alone. A list of just 1,000 items will copy over 1,000,000 elements. Room for algorithm improvment? For sure.
Mark Meyer starts you off on the right foot. If you're going to use recursion, it's best writing your program in functional style, as it will yield the best results. Mixing imperative style (statements, mutations, reassignments, other side effects, etc) with recursion is a recipe for a migraine.
Mark's algorithm, however great a "code improvement", your question is asking for "algorithm improvements". Under this lens, Mark's algorithm suffers from similar intermediate value creation by use of many ...x expressions.
Another lurking offense is the double use of .filter on the same array, rest. This creates an inefficient process as it iterates entirely through rest two (2) times per element. This is a symptom of reaching for low-hanging built-in functions that do close to what you want, but not exactly what you want. A better function would iterate through the array once and return both results.
The inefficiencies in Mark's program are mostly forgivable because of the dramatic improvement in code quality. His program is much more readable than yours because he's using functional style, which is where recursion comes from. The inefficiencies are also very easy to fix, so maybe that's an exercise for you?
Let's see if that gets your brain going. We'll see what answers other people submit before smothering you with too much information.

Your code will fail if we have duplicate elements because of this line.
if (newLast < last) {
It will go into infinite recursion
Refer the snippet with the duplicate array passed as input
function sort(arr) {
if (arr.length === 2) {
const v1 = arr[0];
const v2 = arr[1];
const isGreater = (
(isString(v1) && isString(v2) && v1.toString().toLocaleCompare(v2) > 0) ||
(isNumber(v1) && isNumber(v2) && v1 > v2)
);
return isGreater ? [ v2, v1 ] : [ v1, v2 ];
} else {
const last = arr.pop();
const ret = sort(arr);
const newLast = ret.peekLast();
debugger;
if (newLast < last) {
return [ ...ret, last ];
} else {
return sort( [ last, ...ret ] );
}
}
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return Number.isFinite(value); }
Array.prototype.peekLast = function () { return this.slice().pop(); }
//console.log(sort([1,2,3,4,5]))
console.log(sort([3,3,5,2]))

this one work for me to sort an array recursively:
var array = [3,1,8,2,4,9,16,28];
const sum = (arr, i=0)=> {
if(i === arr.length) return arr;
if(arr[i+1] < arr[i]){
const x = arr[i+1];
arr[i+1] = arr[i];
arr[i] = x;
}
return sum(arr,i+1);
}
console.log(sum(array))

function swap(arr, firstIndex, secondIndex){
let a= arr[firstIndex];
arr[firstIndex] = arr[secondIndex];
arr[secondIndex] = a;
return arr;
}
function sortArr(arr, index=0){
if(index == arr.length) return arr;
for(let i=0;i<arr.length; i++){
if(arr[i] > arr[i+1]){
arr = swap(arr, i, i+1);
}
}
return sortArr(arr, index+1);
}
console.log(sortArr([4,1,3,2,0]));

function quicksort(num){
if (num.length < 2){
return num
}
let pivot = num[0];
let slicedArr = num.slice(1);
let left = [];
let right = [];
for(let i = 0; i < slicedArr.length; i++){
if(slicedArr[i] <= pivot){
left.push(slicedArr[i])
}else{
right.push(slicedArr[i])
}
}
return [...quicksort(left), pivot, ...quicksort(right)]
}

Related

Why does this tail recursive loop cause stack overflow in javascript / node?

exports.tailLoop = function(A) {
const asc = A.sort((a,b) => a - b)
function tailRecur (rest) {
if (!rest.length) return 0
const pair = rest.splice(0,2)
if (pair[0] != pair[1]){
return pair[0]
} else {
return tailRecur(rest)
}
}
return tailRecur(asc)
}
I have also tried this with:
first = rest.shift()
next = rest.shift()
instead of the splice method.
When I input an array with 1 million elements it fails.
Am I not understanding tail recursion correctly or does tail recursion not work on sizes of 1 million (note sort works fine on a 1 million sized array)
To answer the comment question: how to deal with large inputs in node — You can always find ways to turn a recursive function into a non-recursive function. Sometimes it's not as elegant, but in this case, you are basically using recursion for a loop, which would be faster and easier to understand as a simple loop.
Something like:
function nonRec(A){
const asc = A.sort((a,b) => a - b)
while (asc.length){
const pair = asc.splice(0,2)
if (pair[0] != pair[1])
return pair[0]
}
return 0
}
a = [1, 2, 3, 2, 4, 2, 2, 1, 3]
console.log(nonRec(a))
#Mark has already answered the question, so this is merely a refactor of OP's code.
Your code is basically just checking for the two successive items that are equal by looping the array two items a time, this can be drastically optimized by using a for loop to get rid of the expensive calls to splice:
exports.tailLoop = function(A) {
const asc = A.sort((a,b) => a - b);
for(let i = 0; i < asc.length; i += 2) {
if(asc[i] != asc[i + 1]) {
return asc[i];
}
}
return 0;
}
You could try increasing NodeJS maximum call stack, not sure whether this will help in this case.
Another way to skip from the maximum call stack is to change your code from synchronous to asynchronous.
tailLoop = function(A) {
let resolver;
const promise = new Promise((res,_rej)=>{
resolver = res
})
const asc = A.sort((a,b) => a - b)
function tailRecur (rest) {
if (!rest.length) return 0
const pair = rest.splice(0,2)
if (pair[0] != pair[1]){
resolver(pair[0])
} else {
setImmediate(()=>{
tailRecur(rest)
})
}
}
tailRecur(asc)
return promise
}
now it won't exceed maximum call stack.
const a = []
for(let i=0;i<10000;i++){
for(let j=0;j<100;j++){
a.push(0)
}
}
a.push(1)
tailLoop(a).then(result=>{
console.log(result) //1
})
By the way, the code above takes minutes to get the result...
I think you could find a better method/algorithm to solve this problem.

Why is my code pushing every permutation twice?

I'm confused as to why my code is pushing every permutation twice. Please someone help. I'm using heap's algorithm:
var regex = /(.)\1+/g;
function permAlone(str) {
var newArray = str.split('');
var n = newArray.length;
var permutations = [];
var tmp;
function swap(index1, index2) {
tmp = newArray[index1];
newArray[index1] = newArray[index2];
newArray[index2] = tmp;
}
function generate(n, newArray) {
if (n === 1) {
permutations.push(newArray.join(''));
} else {
for(var i = 0; i<n-1; i++) {
generate(n-1, newArray);
swap(n % 2 ? 0 : i, n-1);
permutations.push(newArray.join(''));
}
generate(n-1, newArray);
}
}
generate(n, newArray);
return permutations;
}
permAlone('aab');
The array that is returned is:
["aab", "aab", "aab", "baa", "baa", "aba", "aba", "aba", "baa", "baa"]
So as you can see, the permutations are appearing many more times than intended for each thing. Any help would be great
The code's a little complex and it's difficult to track given the recursion, but if all you want is an array with only unique values, you can simply apply the following code to the result array:
function stripDuplicates(input) {
if (!input || typeof(input) !== 'object' || !('length' in input)) {
throw new Error('input argument is not array.');
}
var newArray = [];
for (var i = 0; i < input.length; i++) {
if (newArray.indexOf(input[i]) === -1) {
newArray.push(input[i]);
}
}
return newArray;
}
This could also be done functionally rather than imperatively, but that's really more of a preference than an optimization issue.
Bálint also points out that you could merely convert the result to a Set, then convert the Set back to an Array, which would automatically strip out any duplicates. Beware, though, that Set is a comparatively new affordance in Javascript and will not function in pre-ES6 environments.
You have a call to:
permutations.push(newArray.join(''));
inside of your for loop. That shouldn't be there. And then, of course if you are permuting strings that have duplicate characters, well, expect to see dupes. e.g., if you permute the string "aa" you'll get two entries from this algorithm "aa" and "aa". Heap's algorithm doesn't try to remove dupes, it treats each element as unique within the string. Obviously, it's trivial to use remove dupes if that's something you care about doing.

Join sequence of arrays with array delimeters ("intersperse")

Is there a function that lets me concat several arrays, with delimiters between them (the delimiters are also arrays), similarly to how join works but not restricted to strings?
The function can be standard JS or part of a major library such as lodash (which is why it's referenced in the tags).
Here is an example of usage:
let numbers = [[1], [2], [3]];
let result = _.joinArrays(numbers, [0]);
console.log(result);
//printed: [1, 0, 2, 0, 3]
This is analogous to:
let strings = ["a", "b", "c"];
let result = strings.join(",");
console.log(result);
//printed: "a,b,c";
However, join can't be used because it turns values into strings, which I don't want to happen.
But it works for any type.
You could simply use array.reduce to concat the arrays, and push what ever you want to use as your delimiter.
let numbers = [[1], [2], [3]];
let n = numbers.reduce((a, b) => a.concat(0, b))
console.log(n)
Matrix Interspersion
Here's the full monty. Go nuts.
var numbers = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
],
delimiters = [
',', '-', 'x'
];
// matrix interspersion, delimiters into numbers's children
// the rank/order/whatevs of the matrix can be arbitrary and variable
numbers.forEach((x, i) => {
for (var j = 1, l = x.length; j <= l; j+=2 )
x.splice(j, 0, delimiters[i]);
})
alert( "Matrix interspersed: " + JSON.stringify(numbers) );
// normal interspersion, a static delimiter into numbers
for (var j = 1, l = numbers.length; j <= l; j+=2 )
numbers.splice(j, 0, ' AND ');
alert( "Outer array interspersed: " + JSON.stringify(numbers) );
// flattening a 2 rank array into a single array
var flattened = Array.prototype.concat.apply([], numbers);
alert( "Flattened: " + JSON.stringify(flattened) );
var result = [].concat.apply([], numbers);
console.log(result)
Here is an implementation of a function that does this, with some extra logic, in case someone else wants to do it. I was really asking about whether this exists already.
Requires lodash.
export function intersperse(arrs, delimeter) {
let joined = [];
for (var i = 0; i < arrs.length; i++) {
let arr = arrs[i];
if (!arr) continue; //handle sparse arrays
joined.push(...arr);
if (i === arrs.length - 1) break;
if (_.isFunction(delimeter)) {
joined.push(...delimeter());
} else if (_.isArray(delimeter)) {
joined.push(...delimeter);
} else {
throw new Error("unknown type");
}
}
return joined;
}
You could use Array#reduce and return an array with the items and glue if necessary.
const join = (array, glue) => array.reduce((a, b) => a.concat(glue, b));
var numbers = [[1], [2], [3]];
console.log(join(numbers, [0]));
console.log(join(numbers, [42, 43]));
console.log(join([[1]], [0]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Many good answers here including 4Castle's comments. As for a change I would like to develop a generic Array.prototype.intersperse() for this job.
In functional JS I could come up with 3 alternatives for this job;
Array.prototype.intersperse_1 = function(s){
return this.reduce((p,c,i) => (p[2*i]=c,p), new Array(2*this.length-1).fill(s));
};
Array.prototype.intersperse_2 = function(s){
return this.reduce((p,c,i) => (i ? p.push(s,c) : p.push(c),p),[]);
};
Array.prototype.intersperse_3 = function(s){
return this.reduce((p,c,i) => i ? p.concat([s],[c]) : p.concat([c]),[]);
};
You should stay away from the 3rd one since .concat() is one of the most expensive operations in functional JS. It will not be able to complete the job for even 100K items.
On the other hand while always being slightly faster in small size arrays, the 1st one turns out to be 2x or even more faster than the 2nd in very large arrays both in FF and Chrome. i.e. 1st intersperses a 10M item array in less than 1000 msec while for the same job the 2nd takes like 2000-2500 msec. Handling this size of course wouldn't be possible with 3rd at all.
So in this particular case it will probably be more expensive compared to the tailored solutions since we have to map the result into primitive values but i guess it's still worth noting the following code. I am sure .concat() adopting tailored solutions will fall behind this when the array length is beyond a certain figure.
Array.prototype.intersperse = function(s){
return this.reduce((p,c,i) => (p[2*i]=c,p), new Array(2*this.length-1).fill(s));
}
var arr = [[1],[2],[3]],
result = arr.intersperse([0])
.map(e => e[0]);
console.log(JSON.stringify(result));
As already mentioned here a few times, the simplest way to do this is
function intercalate(glue, arr){
return arr.reduce((acc, v) => acc.concat(glue, v));
}
but this is not the best way, since it creates with every iteration a new (intermediate) array, that is then thrown away. This doesn't matter for this short array of values, but if you ever intend to use this on a longer Array, you might notice the impact.
Better would be to create one Array and push the values into that, as they come in.
function intercalate(glue, arr){
const push = (acc, v) => (Array.isArray(v)? acc.push(...v): acc.push(v), acc);
return arr.reduce((acc, v, i) => push(i===0? acc: push(acc, glue), v), []);
}
But since this Array is gradually increasing it may still need to allocate a bigger chunk of memory and copy the data. These tasks ar very performant, but still unnecessary (imo); we can do better.
We first create a list containing all Arrays and the delimiter in between, and flatten this by using concat.apply([], list). Therefore we produce one intermediate Array, who's size we can compute ahead of time, and the rest is the problem of Array.concat, and it's underlying implementation.
function intersperse(delimiter, arr){
if(!arr.length) return [];
let j = 0, push = (acc, v) => (acc[j++] = v, acc);
return arr.reduce((acc, v) => push(j===0? acc: push(delimiter, glue), v), Array(2*arr.length-1));
}
//or
function intersperse(delimiter, arr){
if(!arr.length) return [];
var out = Array(2*arr.length-1);
out[0] = arr[0];
for(var i=1, j=1; j<out.length;){
out[j++] = delimiter;
out[j++] = arr[i++];
}
return out;
}
//and
function intercalate(glue, arr){
var emptyArray = [];
return arr.length?
emptyArray.concat.apply(emptyArray, intersperse(glue, arr)):
emptyArray;
}
Wich version will be the best/fastest, in the end, is not that easy to tell, since it may depend on the passed values, and wether or not the JIT compiler optimizes the s*** out of it. I made my points, it's up to you to choose wich version/implementation you use.
To the first version: you may prefer to not put this into a lib at all, but write it mostly inline (it's short and simple enough for that), therefore the JIT-compiler may not try to find some common types between the different calls (#monomorphic function/code) and therefore optimize each occurance seperately. On the other hand, this could be premature optimization. It's up to you.

What is the in-place alternative to Array.prototype.filter()

I've got an array that I would like to remove some elements from. I can't use Array.prototype.filter(), because I want to modify the array in place (because it saves a memory allocation and, more important for me, makes the code more simple in my use case). Is there an in-place alternative to filter that I can use, maybe analogously to how Array.prototype.forEach() can be used as an in-place variant to Array.prototype.map()?
Edit: Minimum example upon request:
function someCallback(array) {
// do some stuff
array.filterInPlace(function(elem) {
var result = /* some logic */
return result;
})
// do some more stuff
}
Is there an in-place alternative to filter
No, but it's not hard to write your own. Here is an approach which squeezes out all the values which fail a condition.
function filterInPlace(a, condition) {
let i = 0, j = 0;
while (i < a.length) {
const val = a[i];
if (condition(val, i, a)) a[j++] = val;
i++;
}
a.length = j;
return a;
}
condition is designed to have the same signature as the callback passed to Array#filter, namely (value, index, array). For complete compatibility with Array#filter, you could also accept a fourth thisArg parameter.
Using forEach
Using forEach has the minor advantage that it will skip empty slots. This version:
Compacts arrays with empty slots
Implements thisArg
Skipps the assignment, if we have not yet encountered a failing element
function filterInPlace(a, condition, thisArg) {
let j = 0;
a.forEach((e, i) => {
if (condition.call(thisArg, e, i, a)) {
if (i!==j) a[j] = e;
j++;
}
});
a.length = j;
return a;
}
a = [ 1,, 3 ];
document.write('<br>[',a,']');
filterInPlace(a, x=>true);
document.write('<br>[',a,'] compaction when nothing changed');
b = [ 1,,3,,5 ];
document.write('<br>[',b,']');
filterInPlace(b, x=>x!==5);
document.write('<br>[',b,'] with 5 removed');
You could use the following:
array.splice(0, array.length,...array.filter(/*YOUR FUNCTION HERE*/))
Explanation:
Splice acts in place
First argument means we start at the start of the array
Second means we delete the entire array
Third means we replace it with its filtered copy
The ... is the spread operator (ES6 only) and changes each member of the array into a separate argument
What you could use
Array#filter returns an array with the same elements, but not necesserily all.
Array#map returns something for each loop, the result is an array with the same length as the source array.
Array#forEach returns nothing, but every element is processed, like above.
Array#reduce returns what ever you want.
Array#some/Array#every returns a boolean value.
But nothing from above is mutating the original array in question of length in situ.
I suggest to use a while loop, beginning from the last element and apply splice to the element, you want to remove.
This keeps the index valid and allows to decrement for every loop.
Example:
var array = [0, 1, 2, 3, 4, 5],
i = array.length;
while (i--) {
if (array[i] % 2) {
array.splice(i, 1);
}
}
console.log(array);
If you are able to add a third-party library, have a look at lodash.remove:
predicate = function(element) {
return element == "to remove"
}
lodash.remove(array, predicate)
The currently selected answer works perfectly fine. However, I wanted this function to be a part of the Array prototype.
Array.prototype.filterInPlace = function(condition, thisArg) {
let j = 0;
this.forEach((el, index) => {
if (condition.call(thisArg, el, index, this)) {
if (index !== j) {
this[j] = el;
}
j++;
}
})
this.length = j;
return this;
}
With this I can just call the function like so:
const arr = [1, 2, 3, 4];
arr.filterInPlace(x => x > 2);
// [1, 2]
I just keep this in a file called Array.js and require it when needed.
A slightly simplified TypeScript variant of user663031's answer:
function filter_in_place<T>(array: Array<T>, condition: (value: T) => boolean)
{
let next_place = 0;
for (let value of array)
{
if (condition(value))
array[next_place++] = value;
}
array.splice(next_place);
}
Using splice() instead of setting the length results in a 1.2x speedup for 1400000 iterations on Chrome 76.

Javascript array sort and unique

I have a JavaScript array like this:
var myData=['237','124','255','124','366','255'];
I need the array elements to be unique and sorted:
myData[0]='124';
myData[1]='237';
myData[2]='255';
myData[3]='366';
Even though the members of array look like integers, they're not integers, since I have already converted each to be string:
var myData[0]=num.toString();
//...and so on.
Is there any way to do all of these tasks in JavaScript?
This is actually very simple. It is much easier to find unique values, if the values are sorted first:
function sort_unique(arr) {
if (arr.length === 0) return arr;
arr = arr.sort(function (a, b) { return a*1 - b*1; });
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) { //Start loop at 1: arr[0] can never be a duplicate
if (arr[i-1] !== arr[i]) {
ret.push(arr[i]);
}
}
return ret;
}
console.log(sort_unique(['237','124','255','124','366','255']));
//["124", "237", "255", "366"]
You can now achieve the result in just one line of code.
Using new Set to reduce the array to unique set of values.
Apply the sort method after to order the string values.
var myData=['237','124','255','124','366','255']
var uniqueAndSorted = [...new Set(myData)].sort()
UPDATED for newer methods introduced in JavaScript since time of question.
This might be adequate in circumstances where you can't define the function in advance (like in a bookmarklet):
myData.sort().filter(function(el,i,a){return i===a.indexOf(el)})
Here's my (more modern) approach using Array.protoype.reduce():
[2, 1, 2, 3].reduce((a, x) => a.includes(x) ? a : [...a, x], []).sort()
// returns [1, 2, 3]
Edit: More performant version as pointed out in the comments:
arr.sort().filter((x, i, a) => !i || x != a[i-1])
function sort_unique(arr) {
return arr.sort().filter(function(el,i,a) {
return (i==a.indexOf(el));
});
}
How about:
array.sort().filter(function(elem, index, arr) {
return index == arr.length - 1 || arr[index + 1] != elem
})
This is similar to #loostro answer but instead of using indexOf which will reiterate the array for each element to verify that is the first found, it just checks that the next element is different than the current.
Try using an external library like underscore
var f = _.compose(_.uniq, function(array) {
return _.sortBy(array, _.identity);
});
var sortedUnique = f(array);
This relies on _.compose, _.uniq, _.sortBy, _.identity
See live example
What is it doing?
We want a function that takes an array and then returns a sorted array with the non-unique entries removed. This function needs to do two things, sorting and making the array unique.
This is a good job for composition, so we compose the unique & sort function together. _.uniq can just be applied on the array with one argument so it's just passed to _.compose
the _.sortBy function needs a sorting conditional functional. it expects a function that returns a value and the array will be sorted on that value. Since the value that we are ordering it by is the value in the array we can just pass the _.identity function.
We now have a composition of a function that (takes an array and returns a unique array) and a function that (takes an array and returns a sorted array, sorted by their values).
We simply apply the composition on the array and we have our uniquely sorted array.
This function doesn't fail for more than two duplicates values:
function unique(arr) {
var a = [];
var l = arr.length;
for(var i=0; i<l; i++) {
for(var j=i+1; j<l; j++) {
// If a[i] is found later in the array
if (arr[i] === arr[j])
j = ++i;
}
a.push(arr[i]);
}
return a;
};
Here is a simple one liner with O(N), no complicated loops necessary.
> Object.keys(['a', 'b', 'a'].reduce((l, r) => l[r] = l, {})).sort()
[ 'a', 'b' ]
Explanation
Original data set, assume its coming in from an external function
const data = ['a', 'b', 'a']
We want to group all the values onto an object as keys as the method of deduplication. So we use reduce with an object as the default value:
[].reduce(fn, {})
The next step is to create a reduce function which will put the values in the array onto the object. The end result is an object with a unique set of keys.
const reduced = data.reduce((l, r) => l[r] = l, {})
We set l[r] = l because in javascript the value of the assignment expression is returned when an assignment statement is used as an expression. l is the accumulator object and r is the key value. You can also use Object.assign(l, { [r]: (l[r] || 0) + 1 }) or something similar to get the count of each value if that was important to you.
Next we want to get the keys of that object
const keys = Object.keys(reduced)
Then simply use the built-in sort
console.log(keys.sort())
Which is the set of unique values of the original array, sorted
['a', 'b']
The solution in a more elegant way.
var myData=['237','124','255','124','366','255'];
console.log(Array.from(new Set(myData)).sort((a,b) => a - b));
I know the question is very old, but maybe someone will come in handy
A way to use a custom sort function
//func has to return 0 in the case in which they are equal
sort_unique = function(arr,func) {
func = func || function (a, b) {
return a*1 - b*1;
};
arr = arr.sort(func);
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (func(arr[i-1],arr[i]) != 0)
ret.push(arr[i]);
}
}
return ret;
}
Example: desc order for an array of objects
MyArray = sort_unique(MyArray , function(a,b){
return b.iterator_internal*1 - a.iterator_internal*1;
});
No redundant "return" array, no ECMA5 built-ins (I'm pretty sure!) and simple to read.
function removeDuplicates(target_array) {
target_array.sort();
var i = 0;
while(i < target_array.length) {
if(target_array[i] === target_array[i+1]) {
target_array.splice(i+1,1);
}
else {
i += 1;
}
}
return target_array;
}
I guess I'll post this answer for some variety. This technique for purging duplicates is something I picked up on for a project in Flash I'm currently working on about a month or so ago.
What you do is make an object and fill it with both a key and a value utilizing each array item. Since duplicate keys are discarded, duplicates are removed.
var nums = [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10];
var newNums = purgeArray(nums);
function purgeArray(ar)
{
var obj = {};
var temp = [];
for(var i=0;i<ar.length;i++)
{
obj[ar[i]] = ar[i];
}
for (var item in obj)
{
temp.push(obj[item]);
}
return temp;
}
There's already 5 other answers, so I don't see a need to post a sorting function.
// Another way, that does not rearrange the original Array
// and spends a little less time handling duplicates.
function uniqueSort(arr, sortby){
var A1= arr.slice();
A1= typeof sortby== 'function'? A1.sort(sortby): A1.sort();
var last= A1.shift(), next, A2= [last];
while(A1.length){
next= A1.shift();
while(next=== last) next= A1.shift();
if(next!=undefined){
A2[A2.length]= next;
last= next;
}
}
return A2;
}
var myData= ['237','124','255','124','366','255','100','1000'];
uniqueSort(myData,function(a,b){return a-b})
// the ordinary sort() returns the same array as the number sort here,
// but some strings of digits do not sort so nicely numerical.
function sort() only is only good if your number has same digit, example:
var myData = ["3","11","1","2"]
will return;
var myData = ["1","11","2","3"]
and here improvement for function from mrmonkington
myData.sort().sort(function(a,b){return a - b;}).filter(function(el,i,a){if(i==a.indexOf(el) & el.length>0)return 1;return 0;})
the above function will also delete empty array and you can checkout the demo below
http://jsbin.com/ahojip/2/edit
O[N^2] solutions are bad, especially when the data is already sorted, there is no need to do two nested loops for removing duplicates. One loop and comparing to the previous element will work great.
A simple solution with O[] of sort() would suffice. My solution is:
function sortUnique(arr, compareFunction) {
let sorted = arr.sort(compareFunction);
let result = sorted.filter(compareFunction
? function(val, i, a) { return (i == 0 || compareFunction(a[i-1], val) != 0); }
: function(val, i, a) { return (i == 0 || a[i-1] !== val); }
);
return result;
}
BTW, can do something like this to have Array.sortUnique() method:
Array.prototype.sortUnique = function(compareFunction) {return sortUnique(this, compareFunction); }
Furthermore, sort() could be modified to remove second element if compare() function returns 0 (equal elements), though that code can become messy (need to revise loop boundaries in the flight). Besides, I stay away from making my own sort() functions in interpreted languages, since it will most certainly degrade the performance. So this addition is for the ECMA 2019+ consideration.
The fastest and simpleness way to do this task.
const N = Math.pow(8, 8)
let data = Array.from({length: N}, () => Math.floor(Math.random() * N))
let newData = {}
let len = data.length
// the magic
while (len--) {
newData[data[len]] = true
}
var array = [2,5,4,2,5,9,4,2,6,9,0,5,4,7,8];
var unique_array = [...new Set(array)]; // [ 2, 5, 4, 9, 6, 0, 7, 8 ]
var uniqueWithSorted = unique_array.sort();
console.log(uniqueWithSorted);
output = [ 0, 2, 4, 5, 6, 7, 8, 9 ]
Here, we used only Set for removing duplicity from the array and then used sort for sorting array in ascending order.
I'm afraid you can't combine these functions, ie. you gotta do something like this:-
myData.unique().sort();
Alternatively you can implement a kind of sortedset (as available in other languages) - which carries both the notion of sorting and removing duplicates, as you require.
Hope this helps.
References:-
Array.sort
Array.unique

Categories

Resources