Related
Just as title reads, I need to check whether the number of unique entries within array exceeds n.
Array.prototype.some() seems to fit perfectly here, as it will stop cycling through the array right at the moment, positive answer is found, so, please, do not suggest the methods that filter out non-unique records and measure the length of resulting dataset as performance matters here.
So far, I use the following code, to check if there's more than n=2 unique numbers:
const res = [1,1,2,1,1,3,1,1,4,1].some((e,_,s,n=2) => s.indexOf(e) != s.lastIndexOf(e) ? false : n-- ? false : true);
console.log(res);
.as-console-wrapper { min-height: 100%}
And it returns false while there's, obviously 3 unique numbers (2,3,4).
Your help to figure out what's my (stupid) mistake here is much appreciated.
p.s. I'm looking for a pure JS solution
You can use a Map() with array values as map keys and count as values. Then iterate over map values to find the count of unique numbers. If count exceeds the limit return true, if not return false.
Time complexity is O(n). It can't get better than O(n) because every number in the array must be visited to find the count of unique numbers.
var data = [1, 1, 2, 1, 1, 3, 1, 1, 4, 1];
function exceedsUniqueLimit(limit) {
var map = new Map();
for (let value of data) {
const count = map.get(value);
if (count) {
map.set(value, count + 1);
} else {
map.set(value, 1);
}
}
var uniqueNumbers = 0;
for (let count of map.values()) {
if (count === 1) {
uniqueNumbers++;
}
if (uniqueNumbers > limit) {
return true;
}
}
return false;
}
console.log(exceedsUniqueLimit(2));
To know if a value is unique or duplicate, the whole array needs to be scanned at least once (Well, on a very large array there could be a test to see how many elements there is left to scan, but the overhead for this kind of test will make it slower)
This version uses two Set
function uniqueLimit(data,limit) {
let
dup = new Set(),
unique = new Set(),
value = null;
for (let i = 0, len = data.length; i < len; ++i) {
value = data[i];
if ( dup.has(value) ) continue;
if ( unique.has(value) ) {
dup.add(value);
unique.delete(value);
continue;
}
unique.add(value);
}
return unique.size > limit;
}
I also tried this version, using arrays:
function uniqueLimit(data, limit) {
let unique=[], dup = [];
for (let idx = 0, len = data.length; idx < len; ++idx) {
const value = data[idx];
if ( dup.indexOf(value) >= 0 ) continue;
const pos = unique.indexOf(value); // get position of value
if ( pos >= 0 ) {
unique.splice(pos,1); // remove value
dup.push(value);
continue;
}
unique.push(value);
}
return unique.length > limit;
};
I tested several of the solutions in this thread, and you can find the result here. If there are only a few unique values, the method by using arrays is the fastest, but if there are many unique values it quickly becomes the slowest, and on large arrays slowest by several magnitudes.
More profiling
I did some more tests with node v12.10.0. The results are normalized after the fastest method for each test.
Worst case scenario: 1000000 entries, all unique:
Set 1.00 // See this answer
Map 1.26 // See answer by Nikhil
Reduce 1.44 // See answer by Bali Balo
Array Infinity // See this answer
Best case scenario: 1000000 entries, all the same:
Array 1.00
Set 1.16
Map 2.60
Reduce 3.43
Question test case: [1, 1, 2, 1, 1, 3, 1, 1, 4, 1]
Array 1.00
Map 1.29
Set 1.47
Reduce 4.25
Another test case: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,
1,1,1,1,1,1,1,3,4,1,1,1,1,1,1,1,2,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,5 ]
Array 1.00
Set 1.13
Map 2.24
Reduce 2.39
Conclusion
The method that uses Set works for both small and large arrays, and performs well regardless of if there are many unique values or not. The version that are using arrays can be faster if there are few unique values, but quickly becomes very slow if there are many unique values.
Using sets, We count hypothetical unique set size and duplicateSet size and delete unique set element for each duplicate found. If unique set size goes below n, we stop iterating.
function uniqueGtN(res, n) {
let uniqSet = new Set(res);
let max = uniqSet.size;
if (max <= n) return false;
let dupSet = new Set();
return !res.some(e => {
if (dupSet.has(e)) {
if (uniqSet.has(e)) {
uniqSet.delete(e);
console.log(...uniqSet);
return (--max <= n);
}
} else {
dupSet.add(e);
}
});
}
console.log(uniqueGtN([1, 1, 2, 1, 1, 3, 3, 1], 2));
From your original solution, I have changed few things, it seems to be working fine:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => {
let firstIndex = s.indexOf(e);
let lastIndex = s.lastIndexOf(e);
// NOT unique
if (firstIndex != lastIndex) {
return false;
}
// unique
return e > n;
});
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
So the shorter version looks like this:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => s.indexOf(e) != s.lastIndexOf(e) ? false : e > n);
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
The code listed in your question does not work because m is not shared across the calls to the some callback function. It is a parameter, and its value is 2 at each iteration.
To fix this, either put m outside, or use the thisArg of the some function (but that means you can't use an arrow function)
let m = 2;
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some((n,i,s) => i > 0 && n == s[i-1] ? !(m--) : false);
// ----- or -----
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some(function(n,i,s) { return i > 0 && n == s[i-1] ? !(this.m--) : false; }, { m: 2 });
Note: this code seems to count if the number of duplicates exceeds a certain value, not the number of unique values.
As another side note, I know you mentioned you did not want to use a duplicate removal algorithm, but performant ones (for example hash-based) would result in something close to O(n).
Here is a solution to count all the values appearing exactly once in the initial array. It is a bit obfuscated and hard to read, but you seem to be wanting something concise. It is the most performant I can think of, using 2 objects to store values seen at least once and the ones seen multiple times:
let res = [1,1,2,3,4].reduce((l, e) => (l[+!l[1][e]][e] = true, l), [{},{}]).map(o => Object.keys(o).length).reduce((more,once) => once-more) > 2;
Here is the less minified version for people who don't like the short version:
let array = [1,1,2,3,4];
let counts = array.reduce((counts, element) => {
if (!counts.atLeastOne[element]) {
counts.atLeastOne[element] = true;
} else {
counts.moreThanOne[element] = true;
}
return counts;
}, { atLeastOne: {}, moreThanOne: {} });
let exactlyOnceCount = Object.keys(counts.atLeastOne).length - Object.keys(counts.moreThanOne).length;
let isOverLimit = exactlyOnceCount > 2;
Whenever I have a type of problem like this, I always like to peek at how the underscore JS folks have done it.
[Ed again: removed _.countBy as it isn't relevant to the answer]
Use the _.uniq function to return a list of unique values in the array:
var u = _.uniq([1,1,2,2,2,3,4,5,5]); // [1,2,3,4,5]
if (u.length > n) { ...};
[ed:] Here's how we might use that implementation to write our own, opposite function that returns only non-unique collection items
function nonUnique(array) {
var result = [];
var seen = [];
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
if (seen.indexOf(value) === -1) { // warning! naive assumption
seen.push(value);
} else {
result.push(value);
}
}
console.log("non-unique result", result);
return result;
};
function hasMoreThanNUnique(array, threshold) {
var uArr = nonUnique(array);
var accum = 0;
for (var i = 0; i < array.length; i++) {
var val = array[i];
if (uArr.indexOf(val) === -1) {
accum++;
}
if (accum > threshold) return true;
}
return false;
}
var testArrA = [1, 1, 2, 2, 2, 3, 4, 5]; // unique values: [3, 4, 5]
var testArrB = [1, 1, 1, 1, 4]; // [4]
var testResultsA = hasMoreThanNUnique(testArrA, 3)
console.log("testArrA and results", testResultsA);
var testResultsB = hasMoreThanNUnique(testArrB, 3);
console.log("testArrB and results", testResultsB);
So far, I came up with the following:
const countNum = [1,1,1,2,1,1,3,1,1,1,4,1,1].reduce((r,n) => (r[n]=(r[n]||0)+1, r), {});
const res = Object.entries(countNum).some(([n,q]) => q == 1 ? !(m--) : false, m=2);
console.log(res);
.as-console-wrapper{min-height:100%}
But I don't really like array->object->array conversion about that. Is there a faster and (at the same time compact) solution?
I have an array like [1,4,3,1,6,5,1,4,4]
Here Highest element frequency is 3 ,I need to select all elements from array that have 3 frequency like [1,4] in above example.
I have tried with this
var count = {},array=[1,4,3,1,6,5,1,4,4],
value;
for (var i = 0; i < array.length; i++) {
value = array[i];
if (value in count) {
count[value]++;
} else {
count[value] = 1;
}
}
console.log(count);
this will output array element with their frequency , now i need all elements that have highest frequency.
I'd approach this problem as follows.
First, write down how you think the problem can be solved IN ENGLISH, or something close to English (or your native language of course!). Write down each step. Start off with a high-level version, such as:
Count the frequency of each element in the input.
Find the highest frequency.
and so on. At this point, it's important that you don't get bogged down in implementation details. Your solution should be applicable to almost any programming language.
Next flesh out each step by adding substeps. For instance, you might write:
Find the highest frequency.
a. Assume the highest frequency is zero.
b. Examine each frequency. If it is higher than the current highest frqeuency, make it the current highest frequency.
Test your algorithm by executing it manually in your head.
Next, convert what you have written about into what is sometimes called pseudo-code. It is at this point that our algorithm starts to look a little bit like a computer program, but is still easily human-readable. We may now use variables to represent things. For instance, we could write "max_freq ← cur_freq". We can refer to arrays, and write loops.
Finally, convert your pseudo-code into JS. If all goes well, it should work the first time around!
In recent years, a lot of people are jumping right into JavaScript, without any exposure to how to think about algorithms, even simple ones. They imagine that somehow they need to be able to, or will magically get to the point where they can, conjure up JS out of thin air, like someone speaking in tongues. In fact, the best programmers do not instantly start writing array.reduce when confronted with a problem; they always go through the process--even if only in their heads--of thinking about the approach to the problem, and this is an approach well worth learning from.
If you do not acquire this skill, you will spend the rest of your career posting to SO each time you can't bend your mind around a problem.
A proposal with Array.prototype.reduce() for a temporary object count, Object.keys() for getting the keys of the temporary object, a Array.prototype.sort() method for ordering the count results and Array.prototype.filter() for getting only the top values with the most count.
Edit: Kudos #Xotic750, now the original values are returned.
var array = [1, 4, 3, 1, 6, 5, 1, 4, 4],
result = function () {
var temp = array.reduce(function (r, a, i) {
r[a] = r[a] || { count: 0, value: a };
r[a].count++;
return r;
}, {});
return Object.keys(temp).sort(function (a, b) {
return temp[b].count - temp[a].count;
}).filter(function (a, _, aa) {
return temp[aa[0]].count === temp[a].count;
}).map(function (a) {
return temp[a].value;
});
}();
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Bonus with a different attempt
var array = [1, 4, 3, 1, 6, 5, 1, 4, 4],
result = array.reduce(function (r, a) {
r.some(function (b, i) {
var p = b.indexOf(a);
if (~p) {
b.splice(p, 1);
r[i + 1] = r[i + 1] || [];
r[i + 1].push(a);
return true;
}
}) || (
r[1] = r[1] || [],
r[1].push(a)
);
return r;
}, []).pop();
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
you can try this
var input = [1,4,3,1,6,5,1,4,4];
var output = {};
for ( var counter = 0; counter < input.length; counter++ )
{
if ( !output[ input[ counter ] ] )
{
output[ input[ counter ] ] = 0;
}
output[ input[ counter ] ]++;
}
var outputArr = [];
for (var key in output)
{
outputArr.push([key, output[key]])
}
outputArr = outputArr.sort(function(a, b) {return b[1] - a[1]})
now initial values of outputArr are the ones with highest frequency
Here is the fiddle
Check this updated fiddle (this will give the output you want)
var input = [1,4,3,1,6,5,1,4,4];
var output = {}; // this object holds the frequency of each value
for ( var counter = 0; counter < input.length; counter++ )
{
if ( !output[ input[ counter ] ] )
{
output[ input[ counter ] ] = 0; //initialized to 0 if value doesn't exists
}
output[ input[ counter ] ]++; //increment the value with each occurence
}
var outputArr = [];
var maxValue = 0;
for (var key in output)
{
if ( output[key] > maxValue )
{
maxValue = output[key]; //find out the max value
}
outputArr.push([key, output[key]])
}
var finalArr = []; //this array holds only those keys whose value is same as the highest value
for ( var counter = 0; counter < outputArr.length; counter++ )
{
if ( outputArr[ counter ][ 1 ] == maxValue )
{
finalArr.push( outputArr[ counter ][ 0 ] )
}
}
console.log( finalArr );
I would do something like this. It's not tested, but it's commented for helping you to understand my approach.
// Declare your array
var initial_array = [1,4,3,1,6,5,1,4,4];
// Declare an auxiliar counter
var counter = {};
// Loop over the array
initial_array.forEach(function(item){
// If the elements is already in counter, we increment the repetition counter.
if counter.hasOwnProperty(item){
counter[item] += 1;
// If the element is not in counter, we set the repetitions to one
}else{
counter[item] = 1;
}
});
// counter = {1 : 3, 4 : 3, 3 : 1, 6 : 1, 5 : 1}
// We move the object keys to an array (sorting it's more easy this way)
var sortable = [];
for (var element in counter)
sortable.push([element, counter[element]]);
// sortable = [ [1,3], [4,3], [3,1], [6,1], [5,1] ]
// Sort the list
sortable.sort(function(a, b) {return a[1] - b[1]})
// sortable = [ [1,3], [4,3], [3,1], [6,1], [5,1] ] sorted, in this case both are equals
// The elements in the firsts positions are the elements that you are looking for
// This auxiliar variable will help you to decide the biggest frequency (not the elements with it)
higgest = 0;
// Here you will append the results
results = [];
// You loop over the sorted list starting for the elements with more frequency
sortable.forEach(function(item){
// this condition works because we have sorted the list previously.
if(item[1] >= higgest){
higgest = item[1];
results.push(item[0]);
}
});
I'm very much with what #torazaburo had to say.
I'm also becoming a fan of ES6 as it creeps more and more into my daily browser. So, here is a solution using ES6 that is working in my browser now.
The shims are loaded to fix browser browser bugs and deficiencies, which is recommended in all environments.
'use strict';
// Your array of values.
const array = [1, 4, 3, 1, 6, 5, 1, 4, 4];
// An ES6 Map, for counting the frequencies of your values.
// Capable of distinguishing all unique values except `+0` and `-0`
// i.e. SameValueZero (see ES6 specification for explanation)
const frequencies = new Map();
// Loop through all the `values` of `array`
for (let item of array) {
// If item exists in frequencies increment the count or set the count to `1`
frequencies.set(item, frequencies.has(item) ? frequencies.get(item) + 1 : 1);
}
// Array to group the frequencies into list of `values`
const groups = [];
// Loop through the frequencies
for (let item of frequencies) {
// The `key` of the `entries` iterator is the value
const value = item[0];
// The `value` of the `entries` iterator is the frequency
const frequency = item[1];
// If the group exists then append the `value`,
// otherwise add a new group containing `value`
if (groups[frequency]) {
groups[frequency].push(value);
} else {
groups[frequency] = [value];
}
}
// The most frequent values are the last item of `groups`
const mostFrequent = groups.pop();
document.getElementById('out').textContent = JSON.stringify(mostFrequent);
console.log(mostFrequent);
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.4.1/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.34.0/es6-shim.js"></script>
<pre id="out"></pre>
you can do like this to find count occurrence each number
var array = [1, 4, 3, 1, 6, 5, 1, 4, 4];
var frequency = array.reduce(function(sum, num) {
if (sum[num]) {
sum[num] = sum[num] + 1;
} else {
sum[num] = 1;
}
return sum;
}, {});
console.log(frequency)
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 1 year ago.
How can I get a list of unique values in an array? Do I always have to use a second array or is there something similar to java's hashmap in JavaScript?
I am going to be using JavaScript and jQuery only. No additional libraries can be used.
Here's a much cleaner solution for ES6 that I see isn't included here. It uses the Set and the spread operator: ...
var a = [1, 1, 2];
[... new Set(a)]
Which returns [1, 2]
Or for those looking for a one-liner (simple and functional) compatible with current browsers:
let a = ["1", "1", "2", "3", "3", "1"];
let unique = a.filter((item, i, ar) => ar.indexOf(item) === i);
console.log(unique);
Update 2021
I would recommend checking out Charles Clayton's answer, as of recent changes to JS there are even more concise ways to do this.
Update 18-04-2017
It appears as though 'Array.prototype.includes' now has widespread support in the latest versions of the mainline browsers (compatibility)
Update 29-07-2015:
There are plans in the works for browsers to support a standardized 'Array.prototype.includes' method, which although does not directly answer this question; is often related.
Usage:
["1", "1", "2", "3", "3", "1"].includes("2"); // true
Pollyfill (browser support, source from mozilla):
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
// c. Increase k by 1.
// NOTE: === provides the correct "SameValueZero" comparison needed here.
if (o[k] === searchElement) {
return true;
}
k++;
}
// 8. Return false
return false;
}
});
}
Since I went on about it in the comments for #Rocket's answer, I may as well provide an example that uses no libraries. This requires two new prototype functions, contains and unique
Array.prototype.contains = function(v) {
for (var i = 0; i < this.length; i++) {
if (this[i] === v) return true;
}
return false;
};
Array.prototype.unique = function() {
var arr = [];
for (var i = 0; i < this.length; i++) {
if (!arr.contains(this[i])) {
arr.push(this[i]);
}
}
return arr;
}
var duplicates = [1, 3, 4, 2, 1, 2, 3, 8];
var uniques = duplicates.unique(); // result = [1,3,4,2,8]
console.log(uniques);
For more reliability, you can replace contains with MDN's indexOf shim and check if each element's indexOf is equal to -1: documentation
One Liner, Pure JavaScript
With ES6 syntax
list = list.filter((x, i, a) => a.indexOf(x) === i)
x --> item in array
i --> index of item
a --> array reference, (in this case "list")
With ES5 syntax
list = list.filter(function (x, i, a) {
return a.indexOf(x) === i;
});
Browser Compatibility: IE9+
Using EcmaScript 2016 you can simply do it like this.
var arr = ["a", "a", "b"];
var uniqueArray = Array.from(new Set(arr)); // Unique Array ['a', 'b'];
Sets are always unique, and using Array.from() you can convert a Set to an array. For reference have a look at the documentations.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
These days, you can use ES6's Set data type to convert your array to a unique Set. Then, if you need to use array methods, you can turn it back into an Array:
var arr = ["a", "a", "b"];
var uniqueSet = new Set(arr); // {"a", "b"}
var uniqueArr = Array.from(uniqueSet); // ["a", "b"]
//Then continue to use array methods:
uniqueArr.join(", "); // "a, b"
If you want to leave the original array intact,
you need a second array to contain the uniqe elements of the first-
Most browsers have Array.prototype.filter:
const unique = array1.filter((item, index, array) => array.indexOf(item) === index);
//if you need a 'shim':
Array.prototype.filter= Array.prototype.filter || function(fun, scope){
var T= this, A= [], i= 0, itm, L= T.length;
if(typeof fun== 'function'){
while(i<L){
if(i in T){
itm= T[i];
if(fun.call(scope, itm, i, T)) A[A.length]= itm;
}
++i;
}
}
return A;
}
Array.prototype.indexOf= Array.prototype.indexOf || function(what, i){
if(!i || typeof i!= 'number') i= 0;
var L= this.length;
while(i<L){
if(this[i]=== what) return i;
++i;
}
return -1;
}
Fast, compact, no nested loops, works with any object not just strings and numbers, takes a predicate, and only 5 lines of code!!
function findUnique(arr, predicate) {
var found = {};
arr.forEach(d => {
found[predicate(d)] = d;
});
return Object.keys(found).map(key => found[key]);
}
Example: To find unique items by type:
var things = [
{ name: 'charm', type: 'quark'},
{ name: 'strange', type: 'quark'},
{ name: 'proton', type: 'boson'},
];
var result = findUnique(things, d => d.type);
// [
// { name: 'charm', type: 'quark'},
// { name: 'proton', type: 'boson'}
// ]
If you want it to find the first unique item instead of the last add a found.hasOwnPropery() check in there.
Not native in Javascript, but plenty of libraries have this method.
Underscore.js's _.uniq(array) (link) works quite well (source).
If you don't need to worry so much about older browsers, this is exactly what Sets are designed for.
The Set object lets you store unique values of any type, whether
primitive values or object references.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const set1 = new Set([1, 2, 3, 4, 5, 1]);
// returns Set(5) {1, 2, 3, 4, 5}
Using jQuery, here's an Array unique function I made:
Array.prototype.unique = function () {
var arr = this;
return $.grep(arr, function (v, i) {
return $.inArray(v, arr) === i;
});
}
console.log([1,2,3,1,2,3].unique()); // [1,2,3]
Short and sweet solution using second array;
var axes2=[1,4,5,2,3,1,2,3,4,5,1,3,4];
var distinct_axes2=[];
for(var i=0;i<axes2.length;i++)
{
var str=axes2[i];
if(distinct_axes2.indexOf(str)==-1)
{
distinct_axes2.push(str);
}
}
console.log("distinct_axes2 : "+distinct_axes2); // distinct_axes2 : 1,4,5,2,3
Majority of the solutions above have a high run time complexity.
Here is the solution that uses reduce and can do the job in O(n) time.
Array.prototype.unique = Array.prototype.unique || function() {
var arr = [];
this.reduce(function (hash, num) {
if(typeof hash[num] === 'undefined') {
hash[num] = 1;
arr.push(num);
}
return hash;
}, {});
return arr;
}
var myArr = [3,1,2,3,3,3];
console.log(myArr.unique()); //[3,1,2];
Note:
This solution is not dependent on reduce. The idea is to create an object map and push unique ones into the array.
You only need vanilla JS to find uniques with Array.some and Array.reduce. With ES2015 syntax it's only 62 characters.
a.reduce((c, v) => b.some(w => w === v) ? c : c.concat(v)), b)
Array.some and Array.reduce are supported in IE9+ and other browsers. Just change the fat arrow functions for regular functions to support in browsers that don't support ES2015 syntax.
var a = [1,2,3];
var b = [4,5,6];
// .reduce can return a subset or superset
var uniques = a.reduce(function(c, v){
// .some stops on the first time the function returns true
return (b.some(function(w){ return w === v; }) ?
// if there's a match, return the array "c"
c :
// if there's no match, then add to the end and return the entire array
c.concat(v)}),
// the second param in .reduce is the starting variable. This is will be "c" the first time it runs.
b);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Another thought of this question. Here is what I did to achieve this with fewer code.
var distinctMap = {};
var testArray = ['John', 'John', 'Jason', 'Jason'];
for (var i = 0; i < testArray.length; i++) {
var value = testArray[i];
distinctMap[value] = '';
};
var unique_values = Object.keys(distinctMap);
console.log(unique_values);
Array.prototype.unique = function () {
var dictionary = {};
var uniqueValues = [];
for (var i = 0; i < this.length; i++) {
if (dictionary[this[i]] == undefined){
dictionary[this[i]] = i;
uniqueValues.push(this[i]);
}
}
return uniqueValues;
}
I have tried this problem in pure JS.
I have followed following steps 1. Sort the given array, 2. loop through the sorted array, 3. Verify previous value and next value with current value
// JS
var inpArr = [1, 5, 5, 4, 3, 3, 2, 2, 2,2, 100, 100, -1];
//sort the given array
inpArr.sort(function(a, b){
return a-b;
});
var finalArr = [];
//loop through the inpArr
for(var i=0; i<inpArr.length; i++){
//check previous and next value
if(inpArr[i-1]!=inpArr[i] && inpArr[i] != inpArr[i+1]){
finalArr.push(inpArr[i]);
}
}
console.log(finalArr);
Demo
You can enter array with duplicates and below method will return array with unique elements.
function getUniqueArray(array){
var uniqueArray = [];
if (array.length > 0) {
uniqueArray[0] = array[0];
}
for(var i = 0; i < array.length; i++){
var isExist = false;
for(var j = 0; j < uniqueArray.length; j++){
if(array[i] == uniqueArray[j]){
isExist = true;
break;
}
else{
isExist = false;
}
}
if(isExist == false){
uniqueArray[uniqueArray.length] = array[i];
}
}
return uniqueArray;
}
Here is an approach with customizable equals function which can be used for primitives as well as for custom objects:
Array.prototype.pushUnique = function(element, equalsPredicate = (l, r) => l == r) {
let res = !this.find(item => equalsPredicate(item, element))
if(res){
this.push(element)
}
return res
}
usage:
//with custom equals for objects
myArrayWithObjects.pushUnique(myObject, (left, right) => left.id == right.id)
//with default equals for primitives
myArrayWithPrimitives.pushUnique(somePrimitive)
I was just thinking if we can use linear search to eliminate the duplicates:
JavaScript:
function getUniqueRadios() {
var x=document.getElementById("QnA");
var ansArray = new Array();
var prev;
for (var i=0;i<x.length;i++)
{
// Check for unique radio button group
if (x.elements[i].type == "radio")
{
// For the first element prev will be null, hence push it into array and set the prev var.
if (prev == null)
{
prev = x.elements[i].name;
ansArray.push(x.elements[i].name);
} else {
// We will only push the next radio element if its not identical to previous.
if (prev != x.elements[i].name)
{
prev = x.elements[i].name;
ansArray.push(x.elements[i].name);
}
}
}
}
alert(ansArray);
}
HTML:
<body>
<form name="QnA" action="" method='post' ">
<input type="radio" name="g1" value="ANSTYPE1"> good </input>
<input type="radio" name="g1" value="ANSTYPE2"> avg </input>
<input type="radio" name="g2" value="ANSTYPE3"> Type1 </input>
<input type="radio" name="g2" value="ANSTYPE2"> Type2 </input>
<input type="submit" value='SUBMIT' onClick="javascript:getUniqueRadios()"></input>
</form>
</body>
I have an array with name "ids" and some values like ['0','567','956','0','34']. Now I need to remove "0" values from this array.
ids.remove ("0"); is not working.
Here's a function that will remove elements of an array with a particular value that won't fail when two consecutive elements have the same value:
function removeElementsWithValue(arr, val) {
var i = arr.length;
while (i--) {
if (arr[i] === val) {
arr.splice(i, 1);
}
}
return arr;
}
var a = [1, 0, 0, 1];
removeElementsWithValue(a, 0);
console.log(a); // [1, 1]
In most browsers (except IE <= 8), you can use the filter() method of Array objects, although be aware that this does return you a new array:
a = a.filter(function(val) {
return val !== 0;
});
Use splice method in javascript. Try this function:
function removeElement(arrayName,arrayElement)
{
for(var i=0; i<arrayName.length;i++ )
{
if(arrayName[i]==arrayElement)
arrayName.splice(i,1);
}
}
Parameters are:
arrayName:- Name of the array.
arrayElement:- Element you want to remove from array
Here's one way to do it:
const array = ['0', '567', '956', '0', '34'];
const filtered = array.filter(Number);
console.log(filtered);
For non-trivial size arrays, it's still vastly quicker to build a new array than splice or filter.
var new_arr = [],
tmp;
for(var i=0, l=old_arr.length; i<l; i++)
{
tmp = old_arr[i];
if( tmp !== '0' )
{
new_arr.push( tmp );
}
}
If you do splice, iterate backwards!
For ES6 best practice standards:
let a = ['0','567','956','0','34'];
a = a.filter(val => val !== "0");
(note that your "id's" are strings inside array, so to check regardless of type you should write "!=")
Below code can solve your problem
for(var i=0; i<ids.length;i++ )
{
if(ids[i]=='0')
ids.splice(i,1);
}
ids.filter(function(x) {return Number(x);});
I believe, the shortest method is
var newList = ['0', '567', '956', '0', '34'].filter(cV => cV != "0")
You could always do,
listWithZeros = ['0', '567', '956', '0', '34']
newList = listWithZeros.filter(cv => cv != "0")
The newList contains your required list.
Explanation
Array.prototype.filter()
This method returns a new array created by filtering out items after testing a conditional function
It takes in one function with possibly 3 parameters.
Syntax:
Array.prototype.filter((currentValue, index, array) => { ... })
The parameters explain themselves.
Read more here.
The easy approach is using splice!!. But there's a problem, every time you remove an element your array size will constantly reduce. So the loop will skip 1 index the array size reduces.
This program will only remove every first zero.
// Wrong approach
let num = [1, 0, 0, 2, 0, 0, 3,];
for(let i=0; i<num.length; i++){
if(num[i]==0)
num.splice(i, 1);
}
console.log(num)
the output will be
[1,0,2,0,3]
So to remove all the zeros you should increase the index if you found the non-zero number.
let i = 0;
while(i<num.length){
if(num[i]==0){
num.splice(i,1);
}
else{
i++;
}
}
But there's a better way. Since changing the size of the array only affects the right side of the array. You can just traverse in reverse and splice.
for(let i=num.length-1; i>=0; i--){
if(num[i]===0)
num.splice(i,1);
}
Say, I have an array that looks like this:
var playlist = [
{artist:"Herbie Hancock", title:"Thrust"},
{artist:"Lalo Schifrin", title:"Shifting Gears"},
{artist:"Faze-O", title:"Riding High"}
];
How can I move an element to another position?
I want to move for example, {artist:"Lalo Schifrin", title:"Shifting Gears"} to the end.
I tried using splice, like this:
var tmp = playlist.splice(2,1);
playlist.splice(2,0,tmp);
But it doesn't work.
The syntax of Array.splice is:
yourArray.splice(index, howmany, element1, /*.....,*/ elementX);
Where:
index is the position in the array you want to start removing elements from
howmany is how many elements you want to remove from index
element1, ..., elementX are elements you want inserted from position index.
This means that splice() can be used to remove elements, add elements, or replace elements in an array, depending on the arguments you pass.
Note that it returns an array of the removed elements.
Something nice and generic would be:
Array.prototype.move = function (from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
Then just use:
var ar = [1,2,3,4,5];
ar.move(0,3);
alert(ar) // 2,3,4,1,5
Diagram:
If you know the indexes you could easily swap the elements, with a simple function like this:
function swapElement(array, indexA, indexB) {
var tmp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = tmp;
}
swapElement(playlist, 1, 2);
// [{"artist":"Herbie Hancock","title":"Thrust"},
// {"artist":"Faze-O","title":"Riding High"},
// {"artist":"Lalo Schifrin","title":"Shifting Gears"}]
Array indexes are just properties of the array object, so you can swap its values.
With ES6 you can do something like this:
const swapPositions = (array, a ,b) => {
[array[a], array[b]] = [array[b], array[a]]
}
let array = [1,2,3,4,5];
swapPositions(array,0,1);
/// => [2, 1, 3, 4, 5]
Here is an immutable version for those who are interested:
function immutableMove(arr, from, to) {
return arr.reduce((prev, current, idx, self) => {
if (from === to) {
prev.push(current);
}
if (idx === from) {
return prev;
}
if (from < to) {
prev.push(current);
}
if (idx === to) {
prev.push(self[from]);
}
if (from > to) {
prev.push(current);
}
return prev;
}, []);
}
You could always use the sort method, if you don't know where the record is at present:
playlist.sort(function (a, b) {
return a.artist == "Lalo Schifrin"
? 1 // Move it down the list
: 0; // Keep it the same
});
Change 2 to 1 as the first parameter in the splice call when removing the element:
var tmp = playlist.splice(1, 1);
playlist.splice(2, 0, tmp[0]);
Immutable version, no side effects (doesn’t mutate original array):
const testArr = [1, 2, 3, 4, 5];
function move(from, to, arr) {
const newArr = [...arr];
const item = newArr.splice(from, 1)[0];
newArr.splice(to, 0, item);
return newArr;
}
console.log(move(3, 1, testArr));
// [1, 4, 2, 3, 5]
codepen: https://codepen.io/mliq/pen/KKNyJZr
EDIT: Please check out Andy's answer as his answer came first and this is solely an extension of his
I know this is an old question, but I think it's worth it to include Array.prototype.sort().
Here's an example from MDN along with the link
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]
Luckily it doesn't only work with numbers:
arr.sort([compareFunction])
compareFunction
Specifies a function that defines the sort order. If omitted, the array is sorted according to each character's Unicode code point value, according to the string conversion of each element.
I noticed that you're ordering them by first name:
let playlist = [
{artist:"Herbie Hancock", title:"Thrust"},
{artist:"Lalo Schifrin", title:"Shifting Gears"},
{artist:"Faze-O", title:"Riding High"}
];
// sort by name
playlist.sort((a, b) => {
if(a.artist < b.artist) { return -1; }
if(a.artist > b.artist) { return 1; }
// else names must be equal
return 0;
});
note that if you wanted to order them by last name you would have to either have a key for both first_name & last_name or do some regex magic, which I can't do XD
Hope that helps :)
Try this:
playlist = playlist.concat(playlist.splice(1, 1));
If you only ever want to move one item from an arbitrary position to the end of the array, this should work:
function toEnd(list, position) {
list.push(list.splice(position, 1));
return list;
}
If you want to move multiple items from some arbitrary position to the end, you can do:
function toEnd(list, from, count) {
list.push.apply(list, list.splice(from, count));
return list;
}
If you want to move multiple items from some arbitrary position to some arbitrary position, try:
function move(list, from, count, to) {
var args = [from > to ? to : to - count, 0];
args.push.apply(args, list.splice(from, count));
list.splice.apply(list, args);
return list;
}
Time complexity of all answers is O(n^2) because had used twice spice. But O(n/2) is possible.
Most Perfomance Solution:
Array with n elements,
x is to, y is from
should be n >x && n > y
time complexity should be |y - x|. So its is number of elements that is between from and to.
bestcase: O(1); //ex: from:4 to:5
average : O(n/2)
worthcase : O(n) //ex: from:0 to:n
function reOrder(from,to,arr) {
if(from == to || from < 0 || to < 0 ) { return arr};
var moveNumber = arr[from];
if(from < to) {
for(var i =from; i< to; i++){
arr[i] = arr[i+1]
}
}
else{
for(var i = from; i > to; i--){
arr[i] = arr[i-1];
}
}
arr[to] = moveNumber;
return arr;
}
var arr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13];
console.log(reOrder(3,7,arr));
As a simple mutable solution you can call splice twice in a row:
playlist.splice(playlist.length - 1, 1, ...playlist.splice(INDEX_TO_MOVE, 1))
On the other hand, a simple inmutable solution could use slice since this method returns a copy of a section from the original array without changing it:
const copy = [...playlist.slice(0, INDEX_TO_MOVE - 1), ...playlist.slice(INDEX_TO_MOVE), ...playlist.slice(INDEX_TO_MOVE - 1, INDEX_TO_MOVE)]
I came here looking for a rearranging complete array, I want something like I did below, but found most of the answers for moving only one element from position A to position B.
Hope my answer will help someone here
function reArrangeArray(firstIndex=0,arr){
var a = [];
var b = []
for(let i = 0; i<= (arr.length-1); i++){
if(i<firstIndex){
a.push(arr[i])
}else{
b.push(arr[i])
}
}
return b.concat(a)
}
const arrayToRearrange = [{name: 'A'},{name: 'B'},{name: 'C'},{name: 'D'},{name: 'E'}];
reArrangeArray(2,arrayToRearrange)
// Output
// [
// { name: 'C' },
// { name: 'D' },
// { name: 'E' },
// { name: 'A' },
// { name: 'B' }
// ]
Reorder its work This Way
var tmpOrder = playlist[oldIndex];
playlist.splice(oldIndex, 1);
playlist.splice(newIndex, 0, tmpOrder);
I hope this will work