Convert first N item in iterable to Array - javascript

Something similar to question Convert ES6 Iterable to Array. But I only want first N items. Is there any built-in for me to do so? Or how can I achieve this more elegantly?
let N = 100;
function *Z() { for (let i = 0; ; i++) yield i; }
// This wont work
// Array.from(Z()).slice(0, N);
// [...Z()].slice(0, N)
// This works, but a built-in may be preferred
let a = [], t = Z(); for (let i = 0; i < N; i++) a.push(t.next().value);

To get the first n values of an iterator, you could use one of:
Array.from({length: n}, function(){ return this.next().value; }, iterator);
Array.from({length: n}, (i => () => i.next().value)(iterator));
To get the iterator of an arbitrary iterable, use:
const iterator = iterable[Symbol.iterator]();
In your case, given a generator function Z:
Array.from({length: 3}, function(){ return this.next().value; }, Z());
If you need this functionality more often, you could create a generator function:
function* take(iterable, length) {
const iterator = iterable[Symbol.iterator]();
while (length-- > 0) yield iterator.next().value;
}
// Example:
const set = new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(...take(set, 3));

There is no built in method to take only a certain number of items from an iterable (ala something like take()). Although your snippet can be somewhat improved with a for of loop, which is specifically meant to work with iterables, eg:
let a = []; let i = 0; for (let x of Z()) { a.push(x); if (++i === N) break; }
Which might be better since your original snippet would continue looping even if there are not N items in the iterable.

A bit shorter and less efficient with .map, and a bit safer with custom function:
function *Z() { for (let i = 0; i < 5; ) yield i++; }
function buffer(t, n = -1, a = [], c) {
while (n-- && (c = t.next(), !c.done)) a.push(c.value); return a; }
const l = console.log, t = Z()
l( [...Array(3)].map(v => t.next().value) )
l( buffer(t) )

how can I achieve this more elegantly?
One possible elegant solution, using iter-ops library:
import {pipe, take} from 'iter-ops';
const i = pipe(
Z(), // your generator result
take(N) // take up to N values
); //=> Iterable<number>
const arr = [...i]; // your resulting array
P.S. I'm the author of the library.

Related

Creating my own array.prototype.map method. How can I access the array?

So I am trying to create a method that mimic exactly what the Array.prototype.map() method does and there is a lot I am confused about.
The main problem I suppose comes down to its syntax. I know there are many different ways to utilitize the map method for instance:
example 1:
let say I have an array of objects -
var movieList = [
{"Title" : "Inception",
"Awards" : "4 oscars",
"Language" : "English",
"imdbRating" : "8.8"
},
{"Title" : "Inception2",
"Awards" : "44 oscars",
"Language" : "English and Spanish",
"imdbRating" : "9.8"
},
{"Title" : "Interstellar",
"Awards" : "10 oscars",
"Language" : "English",
"imdbRating" : "9.5"
}
];
Lets say I want to make a function that returns a list of objects that contains only the title of the movie and its imdbRating. In this case, I can use the map() method:
let newList = movieList.map( (current) ({'title': current['Title'],
'rating': current['imdbRating'] }) );
the above line of code satisfies what i need to achieve my objective using the map method. However, the syntax can be different for other cases
example 2:
let s = [1, 2, 3, 4];
let s2 = s.map( function(item) {
return item*2;
});
using the map function s2 will return an array that has for each element double the value of each element in the s array.
Now, going back to the theme of this post, the problem I am working on gave me this outline to start with:
Array.prototype.myMap = function(callback) {
let newArray = [];
I understand that when an array calls on the myMap method, it is inserting 2 arguments, the array and a function. But I cannot wrap my head around how I can concretely assign the value of each callback function on the element to the newArray in the myMap method. Especially because I do not know how to access the original array.
One of my attempts that I know is ridiculous because I do not know how I can access the length of the array as well as the array itself which calls on the myMap method-
Array.prototype.myMap = function(callback) {
let newArray = [];
let x = this.length();
for(let i=0; i<x; i++){
let counter = callback();
newArray.push(counter);
}
return newArray;
};
the way I've understood the map() method thus far, is it takes 3 arguments, an array, a function, and the element that will be put thru the function and I do not know the syntax well enough to iterate over the array that calls on the map method and nowhere in my course have I been taught how to do this and I have not found any online resource either that offers a solution to this problem.
length is not a method - it's just a property. And you need to pass this[i] to the callback for the correct output.
Array.prototype.myMap = function(callback) {
let newArray = [];
let x = this.length;
for (let i = 0; i < x; i++) {
let counter = callback(this[i]);
newArray.push(counter);
}
return newArray;
};
let arr = [1, 2, 3];
arr = arr.myMap(e => e * 2);
console.log(arr);
Please note it's quite bad practice to mutate the prototype methods - and especially creating a new one when an identical method exists. (map has all the functionality of myMap plus more).
This is simplified version of the actual map Polyfill. You need to get the length using this.length. And pass the current item in loop, it's index, and the array itself as a parameter to callback
Array.prototype.myMap = function(callback, thisArg) {
const newArray = [];
const length = this.length;
for (let i = 0; i < length; i++) {
let value = callback(this[i], i, this); // call with proper arguments
newArray.push(value);
}
return newArray;
};
const arr = [1, 2, 3];
console.log(arr.myMap(a => a * 2))
Note: map method also takes a thisArg as parameter. If you want to use that, you need to call the callback with thisArg as this
callback.call(thisArg, this[i], i, this);
Like this answer, but correctly preserves empty items.
Array.prototype.map = function (callback, thisArg) {
let arr = [];
const len = this.length;
for (let i = 0; i < len; i++) {
if (i in this) arr.push(callback.call(thisArg, this[i], i, this));
else arr.length++;
}
return arr;
};
Here is where you can learn about javascript map function more:
https://www.w3schools.com/jsref/jsref_map.asp
Implementing map method is actually quite easier than you think. Here is a really simple example that would do exactly what the map method do from https://www.w3schools.com/jsref/jsref_map.asp:
Array.prototype.myMap = function(callback) {
arr = [];
for (var i = 0; i < this.length; i++)
arr.push(callback(this[i], i, this));
return arr;
};
//tests
var numbers2 = [1, 4, 9];
var squareRoot = numbers2.myMap(function(num) {
return Math.sqrt(num);
});
console.log(squareRoot); // [ 1, 2, 3 ]
So, the map method simply returns an array by whose values map to the original array's values by running a specified function given as an argument on each of the values.
Note: The method skips the optional parameter of the original map function which specifies what will be the this value
Here is a working version of your myMap function.
A part of imitating these methods that may confuse people is how the this keyword works in javascript. Here is an MDN Reference article.
Array.prototype.myMap = function(callback, thisArg) {
let newArray = [];
let x = this.length;
for(let i=0; i<x; i++){
let counter = callback.call(thisArg, this[i], i, this);
newArray.push(counter);
}
return newArray;
};
Array.prototype.mappy = function (callback, thisArg) {
let newArray = [];
for (let i = 0; i < this.length; i++) {
if (i in this) newArray.push(callback.call(thisArg, this[i], i, this));
else newArray.push(undefined);
}
return newArray;
};
You could take advantage of the spread operator and do something like this:
Map.prototype.slice = function(start, end) {
return new Map([...this].slice(start, end));
};
And in use:
const testMap = new Map([["a", 1], ['b', 2], ["c", 3], ["d", 4], ["e", 5]]);
console.log(testMap); // Map(5) { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }
console.log(testMap.slice(0, 2)); // Map(2) { 'a' => 1, 'b' => 2 }
You can definitely build upon this since this still has the caveats of Array.prototype.slice().
You can also use forEach if you don't want to use a for loop
Array.prototype.myMap = function (callback) {
let newArr = [];
this.forEach((item) => {
let elem = callback(item);
newArr.push(elem);
});
return newArr;
};

How to loop the JavaScript iterator that comes from generator?

Let's assume that we have following generator:
var gen = function* () {
for (var i = 0; i < 10; i++ ) {
yield i;
}
};
What is the most efficient way to loop through the iterator ?
Currently I do it with checking manually if done property is set to true or not:
var item
, iterator = gen();
while (item = iterator.next(), !item.done) {
console.log( item.value );
}
The best way to iterate any iterable (an object which supports ##iterator), is to use for..of, like this
'use strict';
function * gen() {
for (var i = 0; i < 10; i++) {
yield i;
}
}
for (let value of gen()) {
console.log(value);
}
Or, if you want an Array out of it, then you can use Array.from, like this
console.log(Array.from(gen());
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
A self-contained, single-line alternative to the while loop in the question is the for loop. And for the special case of iterating through for pure side effect or depletion, rather than doing something with the results at the end, we avert the issue of binding the next item value, then not using it in a for-of solution:
for (let n = iterator.next(); !n.done; n = iterator.next()) {}

Merging arrays in a particular format [duplicate]

I have two JavaScript arrays:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
I want the output to be:
var array3 = ["Vijendra","Singh","Shakya"];
The output array should have repeated words removed.
How do I merge two arrays in JavaScript so that I get only the unique items from each array in the same order they were inserted into the original arrays?
To just merge the arrays (without removing duplicates)
ES5 version use Array.concat:
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
array1 = array1.concat(array2);
console.log(array1);
ES6 version use destructuring
const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];
Since there is no 'built in' way to remove duplicates (ECMA-262 actually has Array.forEach which would be great for this), we have to do it manually:
Array.prototype.unique = function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
};
Then, to use it:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = array1.concat(array2).unique();
This will also preserve the order of the arrays (i.e, no sorting needed).
Since many people are annoyed about prototype augmentation of Array.prototype and for in loops, here is a less invasive way to use it:
function arrayUnique(array) {
var a = array.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = arrayUnique(array1.concat(array2));
For those who are fortunate enough to work with browsers where ES5 is available, you can use Object.defineProperty like this:
Object.defineProperty(Array.prototype, 'unique', {
enumerable: false,
configurable: false,
writable: false,
value: function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
});
With Underscore.js or Lo-Dash you can do:
console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
http://underscorejs.org/#union
http://lodash.com/docs#union
First concatenate the two arrays, next filter out only the unique items:
var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b)
var d = c.filter((item, pos) => c.indexOf(item) === pos)
console.log(d) // d is [1, 2, 3, 101, 10]
Edit
As suggested a more performance wise solution would be to filter out the unique items in b before concatenating with a:
var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b.filter((item) => a.indexOf(item) < 0))
console.log(c) // c is [1, 2, 3, 101, 10]
[...array1,...array2] // => don't remove duplication
OR
[...new Set([...array1 ,...array2])]; // => remove duplication
This is an ECMAScript 6 solution using spread operator and array generics.
Currently it only works with Firefox, and possibly Internet Explorer Technical Preview.
But if you use Babel, you can have it now.
const input = [
[1, 2, 3],
[101, 2, 1, 10],
[2, 1]
];
const mergeDedupe = (arr) => {
return [...new Set([].concat(...arr))];
}
console.log('output', mergeDedupe(input));
Using a Set (ECMAScript 2015), it will be as simple as that:
const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));
You can do it simply with ECMAScript 6,
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
Use the spread operator for concatenating the array.
Use Set for creating a distinct set of elements.
Again use the spread operator to convert the Set into an array.
Here is a slightly different take on the loop. With some of the optimizations in the latest version of Chrome, it is the fastest method for resolving the union of the two arrays (Chrome 38.0.2111).
JSPerf: "Merge two arrays keeping only unique values" (archived)
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [];
var arr = array1.concat(array2),
len = arr.length;
while (len--) {
var itm = arr[len];
if (array3.indexOf(itm) === -1) {
array3.unshift(itm);
}
}
while loop: ~589k ops/s
filter: ~445k ops/s
lodash: 308k ops/s
for loops: 225k ops/s
A comment pointed out that one of my setup variables was causing my loop to pull ahead of the rest because it didn't have to initialize an empty array to write to. I agree with that, so I've rewritten the test to even the playing field, and included an even faster option.
JSPerf: "Merge two arrays keeping only unique values" (archived)
let whileLoopAlt = function (array1, array2) {
const array3 = array1.slice(0);
let len1 = array1.length;
let len2 = array2.length;
const assoc = {};
while (len1--) {
assoc[array1[len1]] = null;
}
while (len2--) {
let itm = array2[len2];
if (assoc[itm] === undefined) { // Eliminate the indexOf call
array3.push(itm);
assoc[itm] = null;
}
}
return array3;
};
In this alternate solution, I've combined one answer's associative array solution to eliminate the .indexOf() call in the loop which was slowing things down a lot with a second loop, and included some of the other optimizations that other users have suggested in their answers as well.
The top answer here with the double loop on every value (i-1) is still significantly slower. lodash is still doing strong, and I still would recommend it to anyone who doesn't mind adding a library to their project. For those who don't want to, my while loop is still a good answer and the filter answer has a very strong showing here, beating out all on my tests with the latest Canary Chrome (44.0.2360) as of this writing.
Check out Mike's answer and Dan Stocker's answer if you want to step it up a notch in speed. Those are by far the fastest of all results after going through almost all of the viable answers.
I simplified the best of this answer and turned it into a nice function:
function mergeUnique(arr1, arr2){
return arr1.concat(arr2.filter(function (item) {
return arr1.indexOf(item) === -1;
}));
}
The ES6 offers a single-line solution for merging multiple arrays without duplicates by using destructuring and set.
const array1 = ['a','b','c'];
const array2 = ['c','c','d','e'];
const array3 = [...new Set([...array1,...array2])];
console.log(array3); // ["a", "b", "c", "d", "e"]
Just throwing in my two cents.
function mergeStringArrays(a, b){
var hash = {};
var ret = [];
for(var i=0; i < a.length; i++){
var e = a[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
for(var i=0; i < b.length; i++){
var e = b[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
return ret;
}
This is a method I use a lot, it uses an object as a hashlookup table to do the duplicate checking. Assuming that the hash is O(1), then this runs in O(n) where n is a.length + b.length. I honestly have no idea how the browser does the hash, but it performs well on many thousands of data points.
Just steer clear of nested loops (O(n^2)), and .indexOf() (+O(n)).
function merge(a, b) {
var hash = {};
var i;
for (i = 0; i < a.length; i++) {
hash[a[i]] = true;
}
for (i = 0; i < b.length; i++) {
hash[b[i]] = true;
}
return Object.keys(hash);
}
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = merge(array1, array2);
console.log(array3);
I know this question is not about array of objects, but searchers do end up here.
so it's worth adding for future readers a proper ES6 way of merging and then removing duplicates
array of objects:
var arr1 = [ {a: 1}, {a: 2}, {a: 3} ];
var arr2 = [ {a: 1}, {a: 2}, {a: 4} ];
var arr3 = arr1.concat(arr2.filter( ({a}) => !arr1.find(f => f.a == a) ));
// [ {a: 1}, {a: 2}, {a: 3}, {a: 4} ]
EDIT:
The first solution is the fastest only when there are few items. When there are over 400 items, the Set solution becomes the fastest. And when there are 100,000 items, it is a thousand times faster than the first solution.
Considering that performance is important only when there is a lot of items, and that the Set solution is by far the most readable, it should be the right solution in most cases
The perf results below were computed with a small number of items
Based on jsperf, the fastest way (edit: if there are less than 400 items) to merge two arrays in a new one is the following:
for (var i = 0; i < array2.length; i++)
if (array1.indexOf(array2[i]) === -1)
array1.push(array2[i]);
This one is 17% slower:
array2.forEach(v => array1.includes(v) ? null : array1.push(v));
This one is 45% slower (edit: when there is less than 100 items. It is a lot faster when there is a lot of items):
var a = [...new Set([...array1 ,...array2])];
And the accepted answer's is 55% slower (and much longer to write) (edit: and it is several order of magnitude slower than any of the other methods when there are 100,000 items)
var a = array1.concat(array2);
for (var i = 0; i < a.length; ++i) {
for (var j = i + 1; j < a.length; ++j) {
if (a[i] === a[j])
a.splice(j--, 1);
}
}
https://jsbench.me/lxlej18ydg
Array.prototype.merge = function(/* variable number of arrays */){
for(var i = 0; i < arguments.length; i++){
var array = arguments[i];
for(var j = 0; j < array.length; j++){
if(this.indexOf(array[j]) === -1) {
this.push(array[j]);
}
}
}
return this;
};
A much better array merge function.
Performance
Today 2020.10.15 I perform tests on MacOs HighSierra 10.13.6 on Chrome v86, Safari v13.1.2 and Firefox v81 for chosen solutions.
Results
For all browsers
solution H is fast/fastest
solutions L is fast
solution D is fastest on chrome for big arrays
solution G is fast on small arrays
solution M is slowest for small arrays
solutions E are slowest for big arrays
Details
I perform 2 tests cases:
for 2 elements arrays - you can run it HERE
for 10000 elements arrays - you can run it HERE
on solutions
A,
B,
C,
D,
E,
G,
H,
J,
L,
M
presented in below snippet
// https://stackoverflow.com/a/10499519/860099
function A(arr1,arr2) {
return _.union(arr1,arr2)
}
// https://stackoverflow.com/a/53149853/860099
function B(arr1,arr2) {
return _.unionWith(arr1, arr2, _.isEqual);
}
// https://stackoverflow.com/a/27664971/860099
function C(arr1,arr2) {
return [...new Set([...arr1,...arr2])]
}
// https://stackoverflow.com/a/48130841/860099
function D(arr1,arr2) {
return Array.from(new Set(arr1.concat(arr2)))
}
// https://stackoverflow.com/a/23080662/860099
function E(arr1,arr2) {
return arr1.concat(arr2.filter((item) => arr1.indexOf(item) < 0))
}
// https://stackoverflow.com/a/28631880/860099
function G(arr1,arr2) {
var hash = {};
var i;
for (i = 0; i < arr1.length; i++) {
hash[arr1[i]] = true;
}
for (i = 0; i < arr2.length; i++) {
hash[arr2[i]] = true;
}
return Object.keys(hash);
}
// https://stackoverflow.com/a/13847481/860099
function H(a, b){
var hash = {};
var ret = [];
for(var i=0; i < a.length; i++){
var e = a[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
for(var i=0; i < b.length; i++){
var e = b[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
return ret;
}
// https://stackoverflow.com/a/1584377/860099
function J(arr1,arr2) {
function arrayUnique(array) {
var a = array.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
return arrayUnique(arr1.concat(arr2));
}
// https://stackoverflow.com/a/25120770/860099
function L(array1, array2) {
const array3 = array1.slice(0);
let len1 = array1.length;
let len2 = array2.length;
const assoc = {};
while (len1--) {
assoc[array1[len1]] = null;
}
while (len2--) {
let itm = array2[len2];
if (assoc[itm] === undefined) { // Eliminate the indexOf call
array3.push(itm);
assoc[itm] = null;
}
}
return array3;
}
// https://stackoverflow.com/a/39336712/860099
function M(arr1,arr2) {
const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const dedupe = comp(afrom) (createSet);
const union = xs => ys => {
const zs = createSet(xs);
return concat(xs) (
filter(x => zs.has(x)
? false
: zs.add(x)
) (ys));
}
return union(dedupe(arr1)) (arr2)
}
// -------------
// TEST
// -------------
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
[A,B,C,D,E,G,H,J,L,M].forEach(f=> {
console.log(`${f.name} [${f([...array1],[...array2])}]`);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>
This snippet only presents functions used in performance tests - it not perform tests itself!
And here are example test run for chrome
UPDATE
I remove cases F,I,K because they modify input arrays and benchmark gives wrong results
Why don't you use an object? It looks like you're trying to model a set. This won't preserve the order, however.
var set1 = {"Vijendra":true, "Singh":true}
var set2 = {"Singh":true, "Shakya":true}
// Merge second object into first
function merge(set1, set2){
for (var key in set2){
if (set2.hasOwnProperty(key))
set1[key] = set2[key]
}
return set1
}
merge(set1, set2)
// Create set from array
function setify(array){
var result = {}
for (var item in array){
if (array.hasOwnProperty(item))
result[array[item]] = true
}
return result
}
For ES6, just one line:
a = [1, 2, 3, 4]
b = [4, 5]
[...new Set(a.concat(b))] // [1, 2, 3, 4, 5]
The best solution...
You can check directly in the browser console by hitting...
Without duplicate
a = [1, 2, 3];
b = [3, 2, 1, "prince"];
a.concat(b.filter(function(el) {
return a.indexOf(el) === -1;
}));
With duplicate
["prince", "asish", 5].concat(["ravi", 4])
If you want without duplicate you can try a better solution from here - Shouting Code.
[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
return [1, 2, 3].indexOf(el) === -1;
}));
Try on Chrome browser console
f12 > console
Output:
["prince", "asish", 5, "ravi", 4]
[1, 2, 3, "prince"]
My one and a half penny:
Array.prototype.concat_n_dedupe = function(other_array) {
return this
.concat(other_array) // add second
.reduce(function(uniques, item) { // dedupe all
if (uniques.indexOf(item) == -1) {
uniques.push(item);
}
return uniques;
}, []);
};
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var result = array1.concat_n_dedupe(array2);
console.log(result);
There are so many solutions for merging two arrays.
They can be divided into two main categories(except the use of 3rd party libraries like lodash or underscore.js).
a) combine two arrays and remove duplicated items.
b) filter out items before combining them.
Combine two arrays and remove duplicated items
Combining
// mutable operation(array1 is the combined array)
array1.push(...array2);
array1.unshift(...array2);
// immutable operation
const combined = array1.concat(array2);
const combined = [...array1, ...array2]; // ES6
Unifying
There are many ways to unifying an array, I personally suggest below two methods.
// a little bit tricky
const merged = combined.filter((item, index) => combined.indexOf(item) === index);
const merged = [...new Set(combined)];
Filter out items before combining them
There are also many ways, but I personally suggest the below code due to its simplicity.
const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));
You can achieve it simply using Underscore.js's => uniq:
array3 = _.uniq(array1.concat(array2))
console.log(array3)
It will print ["Vijendra", "Singh", "Shakya"].
you can use new Set to remove duplication
[...new Set([...array1 ,...array2])]
New solution ( which uses Array.prototype.indexOf and Array.prototype.concat ):
Array.prototype.uniqueMerge = function( a ) {
for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
if ( this.indexOf( a[i] ) === -1 ) {
nonDuplicates.push( a[i] );
}
}
return this.concat( nonDuplicates )
};
Usage:
>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]
Array.prototype.indexOf ( for internet explorer ):
Array.prototype.indexOf = Array.prototype.indexOf || function(elt)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0) ? Math.ceil(from): Math.floor(from);
if (from < 0)from += len;
for (; from < len; from++)
{
if (from in this && this[from] === elt)return from;
}
return -1;
};
It can be done using Set.
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2);
var tempSet = new Set(array3);
array3 = Array.from(tempSet);
//show output
document.body.querySelector("div").innerHTML = JSON.stringify(array3);
<div style="width:100%;height:4rem;line-height:4rem;background-color:steelblue;color:#DDD;text-align:center;font-family:Calibri" >
temp text
</div>
//Array.indexOf was introduced in javascript 1.6 (ECMA-262)
//We need to implement it explicitly for other browsers,
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt, from)
{
var len = this.length >>> 0;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
//now, on to the problem
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var merged = array1.concat(array2);
var t;
for(i = 0; i < merged.length; i++)
if((t = merged.indexOf(i + 1, merged[i])) != -1)
{
merged.splice(t, 1);
i--;//in case of multiple occurrences
}
Implementation of indexOf method for other browsers is taken from MDC
Array.prototype.add = function(b){
var a = this.concat(); // clone current object
if(!b.push || !b.length) return a; // if b is not an array, or empty, then return a unchanged
if(!a.length) return b.concat(); // if original is empty, return b
// go through all the elements of b
for(var i = 0; i < b.length; i++){
// if b's value is not in a, then add it
if(a.indexOf(b[i]) == -1) a.push(b[i]);
}
return a;
}
// Example:
console.log([1,2,3].add([3, 4, 5])); // will output [1, 2, 3, 4, 5]
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
The nice thing about this one is performance and that you in general, when working with arrays, are chaining methods like filter, map, etc so you can add that line and it will concat and deduplicate array2 with array1 without needing a reference to the later one (when you are chaining methods you don't have), example:
someSource()
.reduce(...)
.filter(...)
.map(...)
// and now you want to concat array2 and deduplicate:
.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
// and keep chaining stuff
.map(...)
.find(...)
// etc
(I don't like to pollute Array.prototype and that would be the only way of respect the chain - defining a new function will break it - so I think something like this is the only way of accomplish that)
A functional approach with ES2015
Following the functional approach a union of two Arrays is just the composition of concat and filter. In order to provide optimal performance we resort to the native Set data type, which is optimized for property lookups.
Anyway, the key question in conjunction with a union function is how to treat duplicates. The following permutations are possible:
Array A + Array B
[unique] + [unique]
[duplicated] + [unique]
[unique] + [duplicated]
[duplicated] + [duplicated]
The first two permutations are easy to handle with a single function. However, the last two are more complicated, since you can't process them as long as you rely on Set lookups. Since switching to plain old Object property lookups would entail a serious performance hit the following implementation just ignores the third and fourth permutation. You would have to build a separate version of union to support them.
// small, reusable auxiliary functions
const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
// de-duplication
const dedupe = comp(afrom) (createSet);
// the actual union function
const union = xs => ys => {
const zs = createSet(xs);
return concat(xs) (
filter(x => zs.has(x)
? false
: zs.add(x)
) (ys));
}
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];
// here we go
console.log( "unique/unique", union(dedupe(xs)) (ys) );
console.log( "duplicated/unique", union(xs) (ys) );
From here on it gets trivial to implement an unionn function, which accepts any number of arrays (inspired by naomik's comments):
// small, reusable auxiliary functions
const uncurry = f => (a, b) => f(a) (b);
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
// union and unionn
const union = xs => ys => {
const zs = createSet(xs);
return concat(xs) (
filter(x => zs.has(x)
? false
: zs.add(x)
) (ys));
}
const unionn = (head, ...tail) => foldl(union) (head) (tail);
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];
const zs = [0,1,2,3,4,5,6,7,8,9];
// here we go
console.log( unionn(xs, ys, zs) );
It turns out unionn is just foldl (aka Array.prototype.reduce), which takes union as its reducer. Note: Since the implementation doesn't use an additional accumulator, it will throw an error when you apply it without arguments.
DeDuplicate single or Merge and DeDuplicate multiple array inputs. Example below.
useing ES6 - Set, for of, destructuring
I wrote this simple function which takes multiple array arguments.
Does pretty much the same as the solution above it just have more practical use case. This function doesn't concatenate duplicate values in to one array only so that it can delete them at some later stage.
SHORT FUNCTION DEFINITION ( only 9 lines )
/**
* This function merging only arrays unique values. It does not merges arrays in to array with duplicate values at any stage.
*
* #params ...args Function accept multiple array input (merges them to single array with no duplicates)
* it also can be used to filter duplicates in single array
*/
function arrayDeDuplicate(...args){
let set = new Set(); // init Set object (available as of ES6)
for(let arr of args){ // for of loops through values
arr.map((value) => { // map adds each value to Set object
set.add(value); // set.add method adds only unique values
});
}
return [...set]; // destructuring set object back to array object
// alternativly we culd use: return Array.from(set);
}
USE EXAMPLE CODEPEN:
// SCENARIO
let a = [1,2,3,4,5,6];
let b = [4,5,6,7,8,9,10,10,10];
let c = [43,23,1,2,3];
let d = ['a','b','c','d'];
let e = ['b','c','d','e'];
// USEAGE
let uniqueArrayAll = arrayDeDuplicate(a, b, c, d, e);
let uniqueArraySingle = arrayDeDuplicate(b);
// OUTPUT
console.log(uniqueArrayAll); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 43, 23, "a", "b", "c", "d", "e"]
console.log(uniqueArraySingle); // [4, 5, 6, 7, 8, 9, 10]

Cycle function in Javascript

I new to Javascript and I am looking for a cycle function. Here's Clojure's implementation I am trying to find a cycle function that infinitely loops/recurses through values of an array. I was hoping to find something like this in the underscore library, but I could not find anything suitable. Ideally I would like to use something like this:
_.head(_.cycle([1,2,3]), 100)
This function would return an array of 100 elements:
[1,2,3,1,2,3,1,2,3,1,2,3,...]
Is there a function like this I can use in Javascript? Here's my feable attempt, but I can't seem to get it to work:
arr = [1,2,3,4,5,6,7,8,9];
var cycle = function(arr) {
arr.forEach(function(d, i) {
if (d === arr.length)
return d
d == 0
else {return d}
});
};
cycle(arr);
You could do something like:
var cycle = function(array, count) {
var output = [];
for (var i = 0; i < count; i++) {
output.push(array[i % array.length]);
}
return output;
}
An implementation of Clojure's cycle :
function cycle(input) {
return function (times) {
var i = 0, output = [];
while (i < times) {
output.push(input[i++ % input.length]);
}
return output;
};
}
Usage examples :
var chars = cycle(['a', 'b']);
chars(0) // []
chars(1) // ["a"]
chars(3) // ["a", "b", "a"]
cycle([1, 2])(3) // [1, 2, 1]
An implementation of Clojure's take :
function take(length, input) {
return typeof input === 'function'
? input(length)
: input.slice(0, length);
}
Usage examples :
take(3, [1, 2, 3, 4]) // [1, 2, 3]
take(3, cycle([1, 2])) // [1, 2, 1]
Both implementations probably do not fit exactly Clojure's versions.
The problem with trying to emulate purely functional in JavaScript is eagerness: JavaScript doesn't have lazy evaluation and hence you can't produce infinite arrays in JavaScript. You need to define a lazy list in JavaScript. This is how I usually do it:
function cons(head, tail) {
return cont({
head: head,
tail: tail
});
}
function cont(a) {
return function (k) {
return k(a);
};
}
The cons function is similar to the cons function in LISP or the : constructor in Haskell. It takes an element and a list and returns a new list with the element inserted at the beginning of the list. The cont function creates a continuation (really useful for reifying thunks to emulate lazy evaluation).
Creating a list using cons is very simple:
var list = cons(1, cons(2, cons(3, cons(4, cons(5, null)))));
var array = [1, 2, 3, 4, 5];
The above list and array are equivalent. We can create two function to convert arrays to lists and vice-versa:
function toList(array) {
var list = null, length = array.length;
while (length) list = cons(array[--length], list);
return list;
}
function toArray(list) {
var array = [];
while (list) {
list = list(id);
array = array.concat(list.head);
list = list.tail;
}
return array;
}
function id(x) {
return x;
}
Now that we have a method of implementing lazy lists in JavaScript let's create the cycle function:
function cycle(list) {
list = list(id);
var head = list.head;
var tail = join(list.tail, cons(head, null));
return function (k) {
return k({
head: head,
tail: cycle(tail)
});
};
}
function join(xs, ys) {
if (xs) {
xs = xs(id);
return cons(xs.head, join(xs.tail, ys));
} else return ys;
}
Now you can create an infinite list as follows:
var list = cycle(toList([1,2,3]));
Let's create a take function to get the first 100 elements of the list:
function take(n, xs) {
if (n > 0) {
xs = xs(id);
return cons(xs.head, take(n - 1, xs.tail));
} else return null;
}
We can now easily get an array of 100 elements with [1,2,3] repeating:
var array = toArray(take(100, list));
Let's see if it works as expected: http://jsfiddle.net/TR9Ma/
To summarize, lazy functional programming in JavaScript is not as much fun as it is in purely functional languages like Haskell. However with a little bit of effort you can make it work.
Here is a slightly more compact version:
function cycle(arr, count) {
for (var i = 0, out = []; i < count; i++) {
out.push(arr[i % arr.length]);
}
return out;
}
And a JSFiddle (outputs the results to the console):
http://jsfiddle.net/2F9hY/1/
Basically just loops through count number of times, getting the i % arr.length item and adding it to the array.
This function should work. You can put the mod operation to good use here.
var cycle = function(input, n) {
var output = [];
for (var i = 0; i < n; i++) {
var j = i % input.length;
output.push(input[j]);
}
return output;
}
Here's a working fiddle: http://jsfiddle.net/K6UhS/1/
Also, I wouldn't introduce a whole library just for this function!
The wu library includes a cycle function which does this:
wu.cycle([ 1, 2, 3 ]).take(10).toArray() // [ 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 ]
If you don't need support for iterators/streams/infinite lists, and just want a function that cycles through an array's values, lei-cycle provides a much more lightweight solution:
const Cycle = require('lei-cycle')
let c = Cycle([ 1, 2, 3 ])
console.log(c()) // 1
console.log(c()) // 2
console.log(c()) // 3
console.log(c()) // 1
// ...
function cycle(array) {
let result = [...array]
result[Symbol.iterator] = function* () {
while (true)
yield* this.values()
}
return result
}
class Cycle {
constructor(array) {
this.array = array;
}
next () {
var x = this.array.shift();
this.array.push(x);
return x;
}
}
const cycle = new Cycle(['a','b','c']);
console.log(cycle.next()); // returns "a"
console.log(cycle.next()); // returns "b"
console.log(cycle.next()); // returns "c"
console.log(cycle.next()); // returns "a"
console.log(cycle.next()); // returns "b"
...

How to merge two arrays in JavaScript and de-duplicate items

I have two JavaScript arrays:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
I want the output to be:
var array3 = ["Vijendra","Singh","Shakya"];
The output array should have repeated words removed.
How do I merge two arrays in JavaScript so that I get only the unique items from each array in the same order they were inserted into the original arrays?
To just merge the arrays (without removing duplicates)
ES5 version use Array.concat:
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
array1 = array1.concat(array2);
console.log(array1);
ES6 version use destructuring
const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];
Since there is no 'built in' way to remove duplicates (ECMA-262 actually has Array.forEach which would be great for this), we have to do it manually:
Array.prototype.unique = function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
};
Then, to use it:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = array1.concat(array2).unique();
This will also preserve the order of the arrays (i.e, no sorting needed).
Since many people are annoyed about prototype augmentation of Array.prototype and for in loops, here is a less invasive way to use it:
function arrayUnique(array) {
var a = array.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = arrayUnique(array1.concat(array2));
For those who are fortunate enough to work with browsers where ES5 is available, you can use Object.defineProperty like this:
Object.defineProperty(Array.prototype, 'unique', {
enumerable: false,
configurable: false,
writable: false,
value: function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
});
With Underscore.js or Lo-Dash you can do:
console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
http://underscorejs.org/#union
http://lodash.com/docs#union
First concatenate the two arrays, next filter out only the unique items:
var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b)
var d = c.filter((item, pos) => c.indexOf(item) === pos)
console.log(d) // d is [1, 2, 3, 101, 10]
Edit
As suggested a more performance wise solution would be to filter out the unique items in b before concatenating with a:
var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b.filter((item) => a.indexOf(item) < 0))
console.log(c) // c is [1, 2, 3, 101, 10]
[...array1,...array2] // => don't remove duplication
OR
[...new Set([...array1 ,...array2])]; // => remove duplication
This is an ECMAScript 6 solution using spread operator and array generics.
Currently it only works with Firefox, and possibly Internet Explorer Technical Preview.
But if you use Babel, you can have it now.
const input = [
[1, 2, 3],
[101, 2, 1, 10],
[2, 1]
];
const mergeDedupe = (arr) => {
return [...new Set([].concat(...arr))];
}
console.log('output', mergeDedupe(input));
Using a Set (ECMAScript 2015), it will be as simple as that:
const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));
You can do it simply with ECMAScript 6,
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
Use the spread operator for concatenating the array.
Use Set for creating a distinct set of elements.
Again use the spread operator to convert the Set into an array.
Here is a slightly different take on the loop. With some of the optimizations in the latest version of Chrome, it is the fastest method for resolving the union of the two arrays (Chrome 38.0.2111).
JSPerf: "Merge two arrays keeping only unique values" (archived)
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [];
var arr = array1.concat(array2),
len = arr.length;
while (len--) {
var itm = arr[len];
if (array3.indexOf(itm) === -1) {
array3.unshift(itm);
}
}
while loop: ~589k ops/s
filter: ~445k ops/s
lodash: 308k ops/s
for loops: 225k ops/s
A comment pointed out that one of my setup variables was causing my loop to pull ahead of the rest because it didn't have to initialize an empty array to write to. I agree with that, so I've rewritten the test to even the playing field, and included an even faster option.
JSPerf: "Merge two arrays keeping only unique values" (archived)
let whileLoopAlt = function (array1, array2) {
const array3 = array1.slice(0);
let len1 = array1.length;
let len2 = array2.length;
const assoc = {};
while (len1--) {
assoc[array1[len1]] = null;
}
while (len2--) {
let itm = array2[len2];
if (assoc[itm] === undefined) { // Eliminate the indexOf call
array3.push(itm);
assoc[itm] = null;
}
}
return array3;
};
In this alternate solution, I've combined one answer's associative array solution to eliminate the .indexOf() call in the loop which was slowing things down a lot with a second loop, and included some of the other optimizations that other users have suggested in their answers as well.
The top answer here with the double loop on every value (i-1) is still significantly slower. lodash is still doing strong, and I still would recommend it to anyone who doesn't mind adding a library to their project. For those who don't want to, my while loop is still a good answer and the filter answer has a very strong showing here, beating out all on my tests with the latest Canary Chrome (44.0.2360) as of this writing.
Check out Mike's answer and Dan Stocker's answer if you want to step it up a notch in speed. Those are by far the fastest of all results after going through almost all of the viable answers.
I simplified the best of this answer and turned it into a nice function:
function mergeUnique(arr1, arr2){
return arr1.concat(arr2.filter(function (item) {
return arr1.indexOf(item) === -1;
}));
}
The ES6 offers a single-line solution for merging multiple arrays without duplicates by using destructuring and set.
const array1 = ['a','b','c'];
const array2 = ['c','c','d','e'];
const array3 = [...new Set([...array1,...array2])];
console.log(array3); // ["a", "b", "c", "d", "e"]
Just throwing in my two cents.
function mergeStringArrays(a, b){
var hash = {};
var ret = [];
for(var i=0; i < a.length; i++){
var e = a[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
for(var i=0; i < b.length; i++){
var e = b[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
return ret;
}
This is a method I use a lot, it uses an object as a hashlookup table to do the duplicate checking. Assuming that the hash is O(1), then this runs in O(n) where n is a.length + b.length. I honestly have no idea how the browser does the hash, but it performs well on many thousands of data points.
Just steer clear of nested loops (O(n^2)), and .indexOf() (+O(n)).
function merge(a, b) {
var hash = {};
var i;
for (i = 0; i < a.length; i++) {
hash[a[i]] = true;
}
for (i = 0; i < b.length; i++) {
hash[b[i]] = true;
}
return Object.keys(hash);
}
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = merge(array1, array2);
console.log(array3);
I know this question is not about array of objects, but searchers do end up here.
so it's worth adding for future readers a proper ES6 way of merging and then removing duplicates
array of objects:
var arr1 = [ {a: 1}, {a: 2}, {a: 3} ];
var arr2 = [ {a: 1}, {a: 2}, {a: 4} ];
var arr3 = arr1.concat(arr2.filter( ({a}) => !arr1.find(f => f.a == a) ));
// [ {a: 1}, {a: 2}, {a: 3}, {a: 4} ]
EDIT:
The first solution is the fastest only when there are few items. When there are over 400 items, the Set solution becomes the fastest. And when there are 100,000 items, it is a thousand times faster than the first solution.
Considering that performance is important only when there is a lot of items, and that the Set solution is by far the most readable, it should be the right solution in most cases
The perf results below were computed with a small number of items
Based on jsperf, the fastest way (edit: if there are less than 400 items) to merge two arrays in a new one is the following:
for (var i = 0; i < array2.length; i++)
if (array1.indexOf(array2[i]) === -1)
array1.push(array2[i]);
This one is 17% slower:
array2.forEach(v => array1.includes(v) ? null : array1.push(v));
This one is 45% slower (edit: when there is less than 100 items. It is a lot faster when there is a lot of items):
var a = [...new Set([...array1 ,...array2])];
And the accepted answer's is 55% slower (and much longer to write) (edit: and it is several order of magnitude slower than any of the other methods when there are 100,000 items)
var a = array1.concat(array2);
for (var i = 0; i < a.length; ++i) {
for (var j = i + 1; j < a.length; ++j) {
if (a[i] === a[j])
a.splice(j--, 1);
}
}
https://jsbench.me/lxlej18ydg
Array.prototype.merge = function(/* variable number of arrays */){
for(var i = 0; i < arguments.length; i++){
var array = arguments[i];
for(var j = 0; j < array.length; j++){
if(this.indexOf(array[j]) === -1) {
this.push(array[j]);
}
}
}
return this;
};
A much better array merge function.
Performance
Today 2020.10.15 I perform tests on MacOs HighSierra 10.13.6 on Chrome v86, Safari v13.1.2 and Firefox v81 for chosen solutions.
Results
For all browsers
solution H is fast/fastest
solutions L is fast
solution D is fastest on chrome for big arrays
solution G is fast on small arrays
solution M is slowest for small arrays
solutions E are slowest for big arrays
Details
I perform 2 tests cases:
for 2 elements arrays - you can run it HERE
for 10000 elements arrays - you can run it HERE
on solutions
A,
B,
C,
D,
E,
G,
H,
J,
L,
M
presented in below snippet
// https://stackoverflow.com/a/10499519/860099
function A(arr1,arr2) {
return _.union(arr1,arr2)
}
// https://stackoverflow.com/a/53149853/860099
function B(arr1,arr2) {
return _.unionWith(arr1, arr2, _.isEqual);
}
// https://stackoverflow.com/a/27664971/860099
function C(arr1,arr2) {
return [...new Set([...arr1,...arr2])]
}
// https://stackoverflow.com/a/48130841/860099
function D(arr1,arr2) {
return Array.from(new Set(arr1.concat(arr2)))
}
// https://stackoverflow.com/a/23080662/860099
function E(arr1,arr2) {
return arr1.concat(arr2.filter((item) => arr1.indexOf(item) < 0))
}
// https://stackoverflow.com/a/28631880/860099
function G(arr1,arr2) {
var hash = {};
var i;
for (i = 0; i < arr1.length; i++) {
hash[arr1[i]] = true;
}
for (i = 0; i < arr2.length; i++) {
hash[arr2[i]] = true;
}
return Object.keys(hash);
}
// https://stackoverflow.com/a/13847481/860099
function H(a, b){
var hash = {};
var ret = [];
for(var i=0; i < a.length; i++){
var e = a[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
for(var i=0; i < b.length; i++){
var e = b[i];
if (!hash[e]){
hash[e] = true;
ret.push(e);
}
}
return ret;
}
// https://stackoverflow.com/a/1584377/860099
function J(arr1,arr2) {
function arrayUnique(array) {
var a = array.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
return arrayUnique(arr1.concat(arr2));
}
// https://stackoverflow.com/a/25120770/860099
function L(array1, array2) {
const array3 = array1.slice(0);
let len1 = array1.length;
let len2 = array2.length;
const assoc = {};
while (len1--) {
assoc[array1[len1]] = null;
}
while (len2--) {
let itm = array2[len2];
if (assoc[itm] === undefined) { // Eliminate the indexOf call
array3.push(itm);
assoc[itm] = null;
}
}
return array3;
}
// https://stackoverflow.com/a/39336712/860099
function M(arr1,arr2) {
const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const dedupe = comp(afrom) (createSet);
const union = xs => ys => {
const zs = createSet(xs);
return concat(xs) (
filter(x => zs.has(x)
? false
: zs.add(x)
) (ys));
}
return union(dedupe(arr1)) (arr2)
}
// -------------
// TEST
// -------------
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
[A,B,C,D,E,G,H,J,L,M].forEach(f=> {
console.log(`${f.name} [${f([...array1],[...array2])}]`);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>
This snippet only presents functions used in performance tests - it not perform tests itself!
And here are example test run for chrome
UPDATE
I remove cases F,I,K because they modify input arrays and benchmark gives wrong results
Why don't you use an object? It looks like you're trying to model a set. This won't preserve the order, however.
var set1 = {"Vijendra":true, "Singh":true}
var set2 = {"Singh":true, "Shakya":true}
// Merge second object into first
function merge(set1, set2){
for (var key in set2){
if (set2.hasOwnProperty(key))
set1[key] = set2[key]
}
return set1
}
merge(set1, set2)
// Create set from array
function setify(array){
var result = {}
for (var item in array){
if (array.hasOwnProperty(item))
result[array[item]] = true
}
return result
}
For ES6, just one line:
a = [1, 2, 3, 4]
b = [4, 5]
[...new Set(a.concat(b))] // [1, 2, 3, 4, 5]
The best solution...
You can check directly in the browser console by hitting...
Without duplicate
a = [1, 2, 3];
b = [3, 2, 1, "prince"];
a.concat(b.filter(function(el) {
return a.indexOf(el) === -1;
}));
With duplicate
["prince", "asish", 5].concat(["ravi", 4])
If you want without duplicate you can try a better solution from here - Shouting Code.
[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
return [1, 2, 3].indexOf(el) === -1;
}));
Try on Chrome browser console
f12 > console
Output:
["prince", "asish", 5, "ravi", 4]
[1, 2, 3, "prince"]
My one and a half penny:
Array.prototype.concat_n_dedupe = function(other_array) {
return this
.concat(other_array) // add second
.reduce(function(uniques, item) { // dedupe all
if (uniques.indexOf(item) == -1) {
uniques.push(item);
}
return uniques;
}, []);
};
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var result = array1.concat_n_dedupe(array2);
console.log(result);
There are so many solutions for merging two arrays.
They can be divided into two main categories(except the use of 3rd party libraries like lodash or underscore.js).
a) combine two arrays and remove duplicated items.
b) filter out items before combining them.
Combine two arrays and remove duplicated items
Combining
// mutable operation(array1 is the combined array)
array1.push(...array2);
array1.unshift(...array2);
// immutable operation
const combined = array1.concat(array2);
const combined = [...array1, ...array2]; // ES6
Unifying
There are many ways to unifying an array, I personally suggest below two methods.
// a little bit tricky
const merged = combined.filter((item, index) => combined.indexOf(item) === index);
const merged = [...new Set(combined)];
Filter out items before combining them
There are also many ways, but I personally suggest the below code due to its simplicity.
const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));
You can achieve it simply using Underscore.js's => uniq:
array3 = _.uniq(array1.concat(array2))
console.log(array3)
It will print ["Vijendra", "Singh", "Shakya"].
you can use new Set to remove duplication
[...new Set([...array1 ,...array2])]
New solution ( which uses Array.prototype.indexOf and Array.prototype.concat ):
Array.prototype.uniqueMerge = function( a ) {
for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
if ( this.indexOf( a[i] ) === -1 ) {
nonDuplicates.push( a[i] );
}
}
return this.concat( nonDuplicates )
};
Usage:
>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]
Array.prototype.indexOf ( for internet explorer ):
Array.prototype.indexOf = Array.prototype.indexOf || function(elt)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0) ? Math.ceil(from): Math.floor(from);
if (from < 0)from += len;
for (; from < len; from++)
{
if (from in this && this[from] === elt)return from;
}
return -1;
};
It can be done using Set.
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2);
var tempSet = new Set(array3);
array3 = Array.from(tempSet);
//show output
document.body.querySelector("div").innerHTML = JSON.stringify(array3);
<div style="width:100%;height:4rem;line-height:4rem;background-color:steelblue;color:#DDD;text-align:center;font-family:Calibri" >
temp text
</div>
//Array.indexOf was introduced in javascript 1.6 (ECMA-262)
//We need to implement it explicitly for other browsers,
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt, from)
{
var len = this.length >>> 0;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
//now, on to the problem
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var merged = array1.concat(array2);
var t;
for(i = 0; i < merged.length; i++)
if((t = merged.indexOf(i + 1, merged[i])) != -1)
{
merged.splice(t, 1);
i--;//in case of multiple occurrences
}
Implementation of indexOf method for other browsers is taken from MDC
Array.prototype.add = function(b){
var a = this.concat(); // clone current object
if(!b.push || !b.length) return a; // if b is not an array, or empty, then return a unchanged
if(!a.length) return b.concat(); // if original is empty, return b
// go through all the elements of b
for(var i = 0; i < b.length; i++){
// if b's value is not in a, then add it
if(a.indexOf(b[i]) == -1) a.push(b[i]);
}
return a;
}
// Example:
console.log([1,2,3].add([3, 4, 5])); // will output [1, 2, 3, 4, 5]
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
The nice thing about this one is performance and that you in general, when working with arrays, are chaining methods like filter, map, etc so you can add that line and it will concat and deduplicate array2 with array1 without needing a reference to the later one (when you are chaining methods you don't have), example:
someSource()
.reduce(...)
.filter(...)
.map(...)
// and now you want to concat array2 and deduplicate:
.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
// and keep chaining stuff
.map(...)
.find(...)
// etc
(I don't like to pollute Array.prototype and that would be the only way of respect the chain - defining a new function will break it - so I think something like this is the only way of accomplish that)
A functional approach with ES2015
Following the functional approach a union of two Arrays is just the composition of concat and filter. In order to provide optimal performance we resort to the native Set data type, which is optimized for property lookups.
Anyway, the key question in conjunction with a union function is how to treat duplicates. The following permutations are possible:
Array A + Array B
[unique] + [unique]
[duplicated] + [unique]
[unique] + [duplicated]
[duplicated] + [duplicated]
The first two permutations are easy to handle with a single function. However, the last two are more complicated, since you can't process them as long as you rely on Set lookups. Since switching to plain old Object property lookups would entail a serious performance hit the following implementation just ignores the third and fourth permutation. You would have to build a separate version of union to support them.
// small, reusable auxiliary functions
const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
// de-duplication
const dedupe = comp(afrom) (createSet);
// the actual union function
const union = xs => ys => {
const zs = createSet(xs);
return concat(xs) (
filter(x => zs.has(x)
? false
: zs.add(x)
) (ys));
}
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];
// here we go
console.log( "unique/unique", union(dedupe(xs)) (ys) );
console.log( "duplicated/unique", union(xs) (ys) );
From here on it gets trivial to implement an unionn function, which accepts any number of arrays (inspired by naomik's comments):
// small, reusable auxiliary functions
const uncurry = f => (a, b) => f(a) (b);
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
// union and unionn
const union = xs => ys => {
const zs = createSet(xs);
return concat(xs) (
filter(x => zs.has(x)
? false
: zs.add(x)
) (ys));
}
const unionn = (head, ...tail) => foldl(union) (head) (tail);
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,4,5,6,6];
const zs = [0,1,2,3,4,5,6,7,8,9];
// here we go
console.log( unionn(xs, ys, zs) );
It turns out unionn is just foldl (aka Array.prototype.reduce), which takes union as its reducer. Note: Since the implementation doesn't use an additional accumulator, it will throw an error when you apply it without arguments.
DeDuplicate single or Merge and DeDuplicate multiple array inputs. Example below.
useing ES6 - Set, for of, destructuring
I wrote this simple function which takes multiple array arguments.
Does pretty much the same as the solution above it just have more practical use case. This function doesn't concatenate duplicate values in to one array only so that it can delete them at some later stage.
SHORT FUNCTION DEFINITION ( only 9 lines )
/**
* This function merging only arrays unique values. It does not merges arrays in to array with duplicate values at any stage.
*
* #params ...args Function accept multiple array input (merges them to single array with no duplicates)
* it also can be used to filter duplicates in single array
*/
function arrayDeDuplicate(...args){
let set = new Set(); // init Set object (available as of ES6)
for(let arr of args){ // for of loops through values
arr.map((value) => { // map adds each value to Set object
set.add(value); // set.add method adds only unique values
});
}
return [...set]; // destructuring set object back to array object
// alternativly we culd use: return Array.from(set);
}
USE EXAMPLE CODEPEN:
// SCENARIO
let a = [1,2,3,4,5,6];
let b = [4,5,6,7,8,9,10,10,10];
let c = [43,23,1,2,3];
let d = ['a','b','c','d'];
let e = ['b','c','d','e'];
// USEAGE
let uniqueArrayAll = arrayDeDuplicate(a, b, c, d, e);
let uniqueArraySingle = arrayDeDuplicate(b);
// OUTPUT
console.log(uniqueArrayAll); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 43, 23, "a", "b", "c", "d", "e"]
console.log(uniqueArraySingle); // [4, 5, 6, 7, 8, 9, 10]

Categories

Resources