how to prevent adding duplicate keys to a javascript array - javascript

I found a lot of related questions with answers talking about for...in loops and using hasOwnProperty but nothing I do works properly. All I want to do is check whether or not a key exists in an array and if not, add it.
I start with an empty array then add keys as the page is scrubbed with jQuery.
Initially, I hoped that something simple like the following would work: (using generic names)
if (!array[key])
array[key] = value;
No go. Followed it up with:
for (var in array) {
if (!array.hasOwnProperty(var))
array[key] = value;
}
Also tried:
if (array.hasOwnProperty(key) == false)
array[key] = value;
None of this has worked. Either nothing is pushed to the array or what I try is no better than simply declaring array[key] = value Why is something so simple so difficult to do. Any ideas to make this work?

Generally speaking, this is better accomplished with an object instead since JavaScript doesn't really have associative arrays:
var foo = { bar: 0 };
Then use in to check for a key:
if ( !( 'bar' in foo ) ) {
foo['bar'] = 42;
}
As was rightly pointed out in the comments below, this method is useful only when your keys will be strings, or items that can be represented as strings (such as numbers).

var a = [1,2,3], b = [4,1,5,2];
b.forEach(function(value){
if (a.indexOf(value)==-1) a.push(value);
});
console.log(a);
// [1, 2, 3, 4, 5]
For more details read up on Array.indexOf.
If you want to rely on jQuery, instead use jQuery.inArray:
$.each(b,function(value){
if ($.inArray(value,a)==-1) a.push(value);
});
If all your values are simply and uniquely representable as strings, however, you should use an Object instead of an Array, for a potentially massive speed increase (as described in the answer by #JonathanSampson).

A better alternative is provided in ES6 using Sets. So, instead of declaring Arrays, it is recommended to use Sets if you need to have an array that shouldn't add duplicates.
var array = new Set();
array.add(1);
array.add(2);
array.add(3);
console.log(array);
// Prints: Set(3) {1, 2, 3}
array.add(2); // does not add any new element
console.log(array);
// Still Prints: Set(3) {1, 2, 3}

If you're already using spread...
let colors = ['red', 'orange', 'yellow'];
let moreColors = ['orange', 'green'];
let mergedColors = [...colors, ...moreColors];
and want to avoid duplicates...
let mergedColors = [...colors, ...moreColors.filter(c => !colors.includes(c)) ];

You can try this:
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
Easiest way to find duplicate values in a JavaScript array

The logic is wrong. Consider this:
x = ["a","b","c"]
x[0] // "a"
x["0"] // "a"
0 in x // true
"0" in x // true
x.hasOwnProperty(0) // true
x.hasOwnProperty("0") // true
There is no reason to loop to check for key (or indices for arrays) existence. Now, values are a different story...
Happy coding

function check (list){
var foundRepeatingValue = false;
var newList = [];
for(i=0;i<list.length;i++){
var thisValue = list[i];
if(i>0){
if(newList.indexOf(thisValue)>-1){
foundRepeatingValue = true;
console.log("getting repeated");
return true;
}
} newList.push(thisValue);
} return false;
}
var list1 = ["dse","dfg","dse"];
check(list1);
Output:
getting repeated
true

let x = "farceus";
let y = "character";
const commonCharacters = function (string1, string2) {
let duplicateCharacter = "";
for (let i = 0; i < string1.length; i += 1) {
if (duplicateCharacter.indexOf(string1[i]) === -1) {
if (string2.indexOf(string1[i]) !== -1) {
duplicateCharacter += string1[i];
}
}
}
return [...duplicateCharacter];
};
console.log(commonCharacters(x, y));

Related

removeDuplicates does not work as expected

I am trying to extend Array prototype with the following functions:
Array.prototype.uniqueItems=function(){
var ar=this,unique=[],max=ar.length;
for(var i = 0 ; i < max;i++)
if(unique.indexOf(ar[i] )==-1)
unique.push(ar[i]);
return unique;
};
Array.prototype.removeDuplicates=function(){
var self = this;
self = this.uniqueItems();
return self;
};
The first function is supposed to return an array without duplicates and the second to remove all duplicates from a given array.
Consider the following code:
var x=[1,1,1,1,2,2,2,2,3,3,3,3,4,4];
x.removeDuplicates();
console.log(x);
The output is :
[ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4 ]
So the duplicates still remain.Any ideas why?Thanks In advance!!!!Also notice that I can not use x= x.uniqueItems() since it will work only for x.I want it to work for any given array.
You might as well do it like this in ES6
var x = [1,1,1,1,2,2,2,2,3,3,3,3,4,4],
u = a => Array.from(new Set(a)),
y = u(x);
console.log(y);
The new Set object in ES6 takes elements and keeps only one of each and discard the dupes. So it's in a way ideal to get unique items. A new set object is instantitated by the Set constructor and it will accept an array of items as an argument such as in var s = new Set([1,1,2,1,4,3,6,7,4,5,6,9,8,1,2,3,4,7,0]) and the resulting s set will be composed of unique entries of 1,2,4,3,6,7,5,9,8 and 0 in the order of appearance. Set objects have generator properties and iterator functions which can be obtained from them. Hence [...s] (the ES6 spread operator) or Array.from(s) would result a proper unique items array automatically.
Converting the Array to a Set and back allows for an arguably cleaner implementation:
Array.prototype.uniqueItems = function() {
return new Array.from(new Set(this));
};
Array.prototype.removeDuplicates = function() {
this.from(new Set(this));
};
Reassign to x.
var x=[1,1,1,1,2,2,2,2,3,3,3,3,4,4];
x = x.removeDuplicates();
console.log(x);
when you say
self = this.uniqueItems();
self is now pointing to a new array, not this
You need to either do
x = x.removeDuplicates();
or iterate this again in removeDuplicates to remove the other items.
For example
Array.prototype.removeDuplicates=function(){
var self = this;
var that = this;
self = this.uniqueItems();
//remove all items
for(var counter = that.length ; counter >= 0; counter -- )
{
that.splice(counter,1);
}
//copy the individual items to this
Object.keys(self).forEach(function(index){
that[index] = self[index]
});
return self;
};
Hey thank you all for your answers .I found a simple solution to my problem by doing the following:
Array.prototype.removeDuplicates=function(){
//First create a copy of the current array without duplicates
var clear_array=this.uniqueItems();
//Delete every item in the current array
this.length=0;
//Fill the current array with the elements of clear_array
for(var i = 0;i<clear_array.length;i++)
this[i] = clear_array[i];
return this;
};
Also I did not use a Set since I want to be sure about backwards compatibility
Hope this will be helpful to someone :)

Compare and fill arrays - JS

a=['a','b','c']
b=['a','c']
I was trying to write a function that compares these both arrays and creates a third array filling the missing array member with null. So the return value should be:
['a',null,'c']
How do I do that?
P.S. Values are always in sequential order.
I am not sharing my try here because I don't want it to be improved or criticized, I know this function is not something hard to write (but it was for me) so I just want answers from experienced people.
You can use the Array.prototype.map function to loop over the a array and replace values.
var a = ['a','b','c'];
var b = ['a','c'];
var c = a.map(function(item) {
return ~b.indexOf(item) ? item : null;
});
console.log(c);
Hint: This only works if a contains at least all of the values that b contains.
A functional, but not necessarily the most efficient, solution:
var results = a.map(function(v) {
return b.indexOf(v) !== -1 ? v : null;
});
As the values are always sequential, it may be more efficient to loop with an index in each array, moving the index forward through b only when a match is found. Whether you actually want to do this as an optimisation depends on how big your arrays are.
Please try the following code,
var result = [];
a.forEach(function(item){
result.push(b.indexOf(item) < 0 ? null : item);
})
A slightly different approach without lookup with indexOf.
var a = ['a', 'b', 'a'],
b = ['a', 'b'],
c = a.map(function () {
var offset = 0;
return function (el, i) {
if (el === b[offset + i]) {
return el;
}
offset--;
return null;
}
}());
document.write('<pre>' + JSON.stringify(c, 0, 4) + '</pre>');

Javascript number of object by different property values in array

Best way to count the number of objects in a array with different object property p values.
function([{"p":"a"},{"p":"b"},{"p":"a"}]){
// some code
}
// in this case return 2
You can use Array.prototype.filter() to keep values that you want. In this case, you must create a variable temp for storing duplicate values and also within the filter function returns true if it does not exist in case. So you have a new array of unique values.
var arr = [{"p":"a"},{"p":"b"},{"p":"a"}], temp = [];
var arrUniques = arr.filter(function(obj){
return temp.indexOf(obj.p) === -1 ? !!temp.push(obj.p) : false
});
alert(arrUniques.length)
With a Map:
var props = new Map();
for (var i = 0; i < array.length; i++) {
var prop = array[i].p,
count = props.get(prop) || 0;
props.set(prop, count + 1);
}
var size = props.size;
If your properties can be safely casted to strings, you can use a common object:
var props = {};
...
var size = Object.keys(props).length;
Otherwise, Map is your answer.
function getUniquePropertyCount(a, p) {
return a.reduce(function (res, el) {
!~res.indexOf(el[p]) && res.push(el[p]);
return res;
}, []).length;
}
document.write(getUniquePropertyCount([{ "p": "a" }, { "p": "b" }, { "p": "a" }], 'p'));
I suppose this is one of those questions where if you ask four programmers, you'll get five answers.
The other answers so far show some interesting approaches. I would watch out for browser compatibility; for example Map() is only available in the newest browsers, not in IE 10 or prior.
So here's one more solution. It's a bit more code, but it's pretty easy to understand, and it works in every browser:
function countUniqueProperties( key, array ) {
var count = 0, values = {};
for( var i = 0; i < array.length; ++i ) {
var value = array[i][key];
if( ! Object.prototype.hasOwnProperty.call( values, value) ) {
++count;
values[value] = true;
}
}
return count;
}
countUniqueProperties( 'p', [ {p:'a'}, {p:'b'}, {p:'a'} ] );
The one complicated part here is the Object.prototype.hasOwnProperty.call(values,value). You could just use values.hasOwnProperty(value), but that would fail if one of your property values was the string "hasOwnProperty":
countUniqueProperties( 'p', [ {p:'a'}, {p:'hasOwnProperty'}, {p:'a'} ] );
Using the longer form avoids this issue.
lodash is nice for things like this.
Use
_.uniq([{"p":"a"},{"p":"b"},{"p":"a"}]).length
and it'll return 2.

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

Another javascript array challenge

Solving another array manipulation, and I'm taking longer than usual to solve this. I need help in combining array values:
var array1 = ["alpha|LJ", "bravo|MH", "charlie|MH", "delta|MF",
"echo|16", "{foxtrot}|GG", "{golf}|HS"];
var array2 = ["charlie-{golf}-{foxtrot}", "echo-{golf}"]; //some templates
such that the final array be:
final_array = ["alpha-LJ", "bravo-MH", "charlie-HS-GG-MH", "delta-MF",
"echo-HS-16"];
To make it clear how I arrived with the final_array, alpha, bravo and delta only got their "|" replaced with "-" since they are not found on my array2 template. charlie and echo got the template so the respective values of the {} were replaced based on array1. Array1 honestly is not the best key:value relationship that I could come up for now.
Here are some requirementL:
* Anything in array1 with {} braces are not meant to be templated.
* Keywords in array2 will always have a matching value in array1.
I've read about jquery .map() and thinking that it is achievable using this, maybe together with Regexp. Hope you'll utilize these. Also, if it helps, final_array can be of any order.
I really need to up my knowledge on these two topics... :|
Thank you in advance.
Edit: Updated to match your output and comment some of the madness. This doesn't feel like it's the most efficient, given the split() done to values at the start and then again at the end...but it works.
function funkyTransform( values, templates ){
// Make a copy of the array we were given so we can mutate it
// without rudely changing something passed to our function.
var result = values.concat();
// Map {value} entries for later lookup, and throw them out of the result
var valueMap = {};
for (var i=result.length-1;i>=0;--i){
var pair = result[i].split('|');
if (pair[0][0]=="{"){
valueMap[pair[0]] = pair[1];
result.splice(i,1); // Yank this from the result
}
}
console.log(valueMap);
// {
// "{foxtrot}": "GG",
// "{golf}": "HS"
// }
// Use the value map to replace text in our "templates", and
// create a map from the first part of the template to the rest.
// THIS SHOULD REALLY SCAN THE TEMPLATE FOR "{...}" PIECES
// AND LOOK THEM UP IN THE MAP; OOPS O(N^2)
var templateMap = {};
for (var i=templates.length-1;i>=0;--i){
var template = templates[i];
for (var name in valueMap){
if (valueMap.hasOwnProperty(name)){
template = template.replace(name,valueMap[name]);
}
}
var templateName = template.split('-')[0];
templateMap[ templateName ] = template.slice(templateName.length+1);
}
console.log(templateMap);
// {
// "charlie": "HS-GG",
// "echo": "HS"
// }
// Go through the results again, replacing template text from the templateMap
for (var i=result.length-1;i>=0;--i){
var pieces = result[i].split('|');
var template = templateMap[pieces[0]];
if (template) pieces.splice(1,0,template);
result[i] = pieces.join('-');
}
return result;
}
var output = funkyTransform( array1, array2 );
console.log(output);
// ["alpha-LJ", "bravo-MH", "charlie-HS-GG-MH", "delta-MF", "echo-HS-16"]
This managed to get your desired output, though I made a few assumptions:
Anything in array1 with {} braces are not meant to be templated.
Keywords in array2 will always have a matching value in array1 (this can easily be changed, but not sure what your rule would be).
Code:
// This is the main code
var final_array = $.map(array1, function (item) {
var components = item.split('|');
// Ignore elements between {} braces
if (/^\{.*\}$/.test(components[0])) return;
components[0] = template(components[0]);
return components.join('-');
});
// Helper to lookup array2 for a particular string and template it
// with the values from array1
function template(str) {
var index = indexOfMatching(array2, str, '-');
if (index == -1) return str;
var components = array2[index].split('-');
var result = [str];
for (var i = 1; i < components.length; i++) {
result.push(array1[indexOfMatching(array1, components[i], '|')]
.split('|')[1]);
}
return result.join('-');
}
// Helper to for looking up array1 and array2
function indexOfMatching(array, target, separator) {
for (var i = 0; i < array.length; i++) {
if (array[i].split(separator)[0] === target) return i;
}
return -1;
}

Categories

Resources