Compare an unspecified number of arrays for common values in javascript - javascript

I would like to know how to compare two or more -- potentially unlimited -- arrays for common values and push these values into a new array efficiently. Below I have a function that will accept unlimited arguments, but I am uncertain if this is a good place to begin. PHP appears to have a method that can do what I want called array_intersect. Does javascript offer something similar?
Note: I have found examples of how this can be done with two or so arrays, but I have not found examples of how such approaches might be applied to an unspecified number of arrays as of yet. Therefore I do not see this as a duplicate question.
To further clarify, the arrays might be filled with anything. Letters, numbers, symbols, words, you name it, it might be there.
var sampleOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var sampleTwo = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
function FindDirectRelation() {
for(var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
};
};
var directRelation = FindDirectRelation(sampleOne, sampleTwo);
I am still a coding novice, so please ensure that everything is explained in a way that is simple enough for me to understand.

using an existing intersect that works with 2 arrays, we can chain together a common sub-set using the built-in reduce() method on an array of arrays that need intersected:
function intersect(a, b) {
var aa = {};
a.forEach(function(v) { aa[v]=1; });
return b.filter(function(v) { return v in aa; });
}
var r1=[1,2,3],
r2=[1,3,4,5],
r3=[5,1,3];
alert([r1, r2, r3].reduce(intersect)) // shows: 1,3
if you define "intersect" as just being in more than one array (not every), then it's more complex...

Check to make sure the elements in the first array are also in the remaining arrays:
function multi_intersect(a) {
var other_arrays = Array.prototype.slice.call(arguments, 1);
return a . filter(function(elt) {
return other_arrays.every(function(an) {
return an.indexOf(elt) !== -1;
});
});
}

Try using Array.prototype.filter() , Array.prototype.indexOf()
var res = sampleOne.filter(function(val) {return sampleTwo.indexOf(val) !== -1})
var sampleOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var sampleTwo = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
var arr = ["a", "b", "c"];
var arr1 = ["c", "d", "e"];
var arr2 = [2, 7];
function samples() {
var args = Array.prototype.slice.call(arguments);
var res = [];
for (var i = 0, curr, next; i < args.length; i++) {
if (args[i + 1]) {
// set `curr` to array `i`
curr = args[i];
// set `next` to array `i + 1` if it exists
next = args[i + 1]
} else {
// if at last index, set `curr` to `args` : input arrays
// flattened to single array , with element at `i` removed
curr = [].concat.apply([], args.slice(0, args.length - 1));
console.log(curr)
// set next to current index
next = args[i];
};
next = next.filter(function(val) {
return curr.indexOf(val) !== -1
// filter duplicate entries at `res`
&& res.indexOf(val) === -1
});
res = res.concat.apply(res, next);
};
return res
}
var sample = samples(sampleOne, sampleTwo, arr, arr1, arr2);
console.log(sample); // [5, 6, 7, 8, 9, 10, 11, 12, "c", 2]

Related

how to split an array according to their difference in value

I have an ordered array like the following:
const arrayToSplit = [1, 3, 5, 8, 10, 12, 21, 23];
which I want to be split into multiple arrays where the difference between one and another is max a value, let us say 2:
const result = [
[1, 3, 5],
[8, 10, 12],
[21, 23]
];
or 8:
const result = [
[1, 3, 5, 8, 10, 12],
[21, 23]
];
and so forth. I have a solution but is far away from an elegant one, where I run the array by using an accumulator variable. Is there a neater, more elegant solution?
A possible solution to this:
function splitArray(arrayToSplit = []){
const explodedArray = [];
let elementsNewArray = [];
for(const element of arrayToSplit) {
if(elementsNewArray.length == 0) {
elementsNewArray.push(element);
continue;
}
if(element - _.last(elementsNewArray)) <= 2) {
elementsNewArray.push(element);
} else {
explodedArray.push(elementsNewArray);
elementsNewArray = [element];
}
}
explodedArray.push(elementsNewArray);
return explodedArray;
}
It seems reasonably straightforward to simply keep track of the last item iterated over in a variable. When iterating, check if the difference between that and the current item being iterated is larger than the limit. If it is, push a new array. Regardless. push the item being iterated over to the last array in the collection.
const arrayToSplit = [1, 3, 5, 8, 10, 12, 21, 23];
const maxDiff = 2;
const result = [[arrayToSplit[0]]];
let last = arrayToSplit[0];
for (const item of arrayToSplit.slice(1)) {
if (item - last > maxDiff) result.push([]);
result[result.length - 1].push(item);
last = item;
}
console.log(result);

Delete numbers below a certain number in an array

Today i'm facing a really weird problem.
I'm actually trying to delete numbers below a certain number in an array.
I have this array [1, 7, 2, 3, 90, 4, 70, 20] and I only want the numbers greater than 20 !
So I should have that in output : [90, 70]
But Instead I have this array : [7, 3, 90, 70, 20] ???
Here is my code :
function superior() {
var arr = [1, 7, 2, 3, 90, 4, 70, 20]
for (var i = 0; i < arr.length; i++) {
if (arr[i] < 20) {
arr.splice(arr.indexOf(arr[i]), 1);
} else {
break;
}
}
return arr;
}
console.log(superior());
Mutating an array while you're looping through it is always going to be tricky. If you shrink the size of the array, but don't change i, then you're can end up interacting with the wrong element of the array the next time through the loop. Also, using break will stop the loop from running entirely, and so skip the rest of the array. You may have meant to use continue, but since you're on the last line of the loop anyway, continue isn't needed.
Rather than trying to change the array and loop through it at the same time, i'd recommend creating a new array. Then you can loop through the old one unhindered:
const arr = [1, 7, 2, 3, 90, 4, 70, 20]
const newArr = []
for (const i = 0; i < arr.length; i++) {
if (arr[i] >= 20) {
newArr.push(arr[i]);
}
}
console.log(newArr)
Filtering an array like this is a very common thing to do, so there are built in tools to do it for you. Every array has a .filter method. You pass into it a function describing what elements you want to keep, and it will produce the new array for you:
const arr = [1, 7, 2, 3, 90, 4, 70, 20]
const newArr = arr.filter(element => element >= 20);
console.log(newArr)
You can filter them out according to a condition.
And replace your existing array with the filtered one. Or if you don't want to replace it, use another variable and assign the filtered value to that variable.
var newArray = array.filter(item => item > 20)
Check .filter()
var array = [1, 7, 2, 3, 90, 4, 70, 20];
array = array.filter(item => item > 20)
console.log(array)
You can use Array.filter()
var arr = [1, 7, 2, 3, 90, 4, 70, 20]
var filteredArr = arr.filter(item => item > 20);
console.log(filteredArr);
You could iterate from the end and omit indexOf, because you have already the index i.
This appoach loops from the end and after splicing, the remaining lower indices remains.
function superior() {
var array = [1, 7, 2, 3, 90, 4, 70, 20],
i = array.length;
while (i--) if (array[i] < 20) array.splice(i, 1);
return array;
}
console.log(superior());
Use temporary array to push new values.
function superior() {
var arr = [1, 7, 2, 3, 90, 4, 70, 20];
temp_arr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 20) {
temp_arr.push(arr[i]);
}
}
return temp_arr;
}
console.log(superior());

Iterate through array by sets of [unknown integer] [duplicate]

This question already has answers here:
Split array into chunks
(73 answers)
Closed 5 years ago.
Assuming I have an integer n which is greater than 0, and an array like this:
var array = [1, 2, 5, 6, 8, 9, 12, 13, 17...] //random values
How would I iterate through this array, going through and getting values n at a time (and putting it into a 2D array as well)?
If n were 3, for example, I would want a return value of
[[1, 2, 5], [6, 8, 9], [12, 13, 17]...]
And the code would be like this:
var array = [];
for (var i = 0; i < array.length; i += 3) {
var first = array[i];
var second = array[i+1];
var third = array[i+2];
array.push([
first, second, third
]);
}
Problem with this is that I have fixed values to get my objects by (the i, i+1, etc.)
If I have an unknown integer, then incrementing right up to n will not work.
How would I go about achieving this?
Use slice to take chunks and go through the array:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
const partition = (n, arr) => {
const result = [];
let i = 0;
while(i < arr.length) {
result.push(arr.slice(i, i + n));
i = i + n;
}
return result;
};
console.log(partition(1, arr));
console.log(partition(2, arr));
console.log(partition(3, arr));
console.log(partition(4, arr));

Comparing Multiple Arrays Using Reduce

pretty new to Javascript and I've tried this question about 4 times now in a span of about a month and I am still unable to solve it.
So here is the question:
Construct a function intersection that compares input arrays and returns a new array with elements found in all of the inputs. BONUS: Use reduce!
The format is:
function intersection(arrays) {
// Your Code Goes Here
}
Test Case: Should log [15, 5]
console.log('Extensions 3 Test: ' + intersection([5, 10, 15, 20], [15, 88, 1, 5, 7]/*, [1, 10, 15, 5, 20]*/));
My current solution: Works for the case of only have two items to compare, but not for the third one, I could make it so that I would loop through and compare the obtained values with the next array but I don't think I am on the right path... Also, I am not using reduce to implement it... And I am not sure if I am supposed to be using 'arguments.' Any help is appreciated! Thank you so much.
function intersection(arrays) {
array = [];
for (var i = 0; i < arguments.length; i++)
array.push(arguments[i]);
var result = [];
for(var i = 0; i < array.length - 1; i++) {
for(var j = 0; j < array[i].length; j++) {
if (array[i+1].includes(array[i][j]))
result.push(array[i][j]);
}
}
return result;
}
Although, as several suggestions said, you could use underscore, lodash, or my personal favorite, Ramda (disclaimer: I'm one of the authors), this function should be straightforward enough that you wouldn't even consider a library for it. Here's a simple version:
const intersection = (xs, ys) => xs.filter(x => ys.indexOf(x) > -1);
intersection([5, 10, 15, 20, 3], [15, 88, 3, 1, 5, 7]); //=> [5, 15, 3]
const intersectAll = (...xss) => xss.reduce(intersection);
intersectAll([5, 10, 15, 20, 3], [15, 88, 3, 1, 5, 7], [1, 10, 15, 5, 20]); //=> [5, 15]
I would think that this is all you need, at least so long as you're worried only about reference/primitive equality and don't need to consider cases where you want to know that {x: 1} and {x: 1} are the same, even though they aren't the same reference. If you do need that, you might look to Ramda's intersection function.
Note that if includes were better supported, I would recommend this version instead, as it reads better:
const intersection = (xs, ys) => xs.filter(x => ys.includes(x));
Also, if you have no need for the binary function, you can make just a variadic version of it by combining the two above:
const intersection = (...xss) => xss.reduce((xs, ys) => xs.filter(x => ys.indexOf(x) > -1));
Maybe someone will finds it useful.
As an argument to the function you can give any number of arrays of any length and the function is compact, I think ;)
const findSimilar = (...arrays) => {
return arrays.reduce((includ, current) =>
Array.from(new Set(includ.filter((a) => current.includes(a))))
);
};
console.log(
findSimilar([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20])
);
And how it works:
Ok, first u take rest parameters(...arrays) as parameter of function, so u have
arrays = [ [5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20] ]
then in first iteration of reduce we have
includ = [5, 10, 15, 20] and current = [15, 88, 1, 5, 7]
on this two we use filter, what give us [5,15], i use Set to make shure there is no repetition and make array back (Array.from()), which is passed to the next iteration of reduce as "includ", at the next iteration we have
incude = [5,15] and current = [1, 10, 15, 5, 20] and so on ...
We can even use it like this
let result = [
[5, 10, 15, 20],
[15, 88, 1, 5, 7],
[1, 10, 15, 5, 20]
].reduce((includ, current) =>
Array.from(new Set(includ.filter((a) => current.includes(a))))
);
console.log(result);
Although not solving your problem directly, you can do what you're trying to do using the opensource library underscore.js.
_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
You may be able to derive inspiration from the way that's been implemented. The above is the function call to their own _.intersection function which is also dependent on other underscore.js functions as you see below:
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
if (array == null) return [];
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = array.length; i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
for (var j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
};
Here is a solution using reduce, with the empty array passed in as intersection as the initial value.
Iterate the numbers and check if each one appears in one of the subarrays.
If it doesn't, set the Boolean isPresentInAll to false.
If it does appear in all three and it's not already present in the
intersection array, then push to the intersection array.
function intersection(arrayOfArrays) {
return arrayOfArrays.reduce(function(intersection, subArray) {
subArray.forEach(function(number) {
var isPresentInAll = true;
for (var i = 0; i < arrayOfArrays.length; i++) {
if (arrayOfArrays[i].indexOf(number) === -1) {
isPresentInAll = false;
}
}
if (isPresentInAll === true && intersection.indexOf(number) === -1) {
intersection.push(number);
}
});
return intersection;
}, []);
}
I think i got the right function for you.
(Note: results are not sorted!)
var intersection = function() {
// merge deduped arrays from arguments
var arrays = Array.prototype.reduce.call(arguments, function(carry, array) {
return [].concat(carry, array.filter(function(item, index, origin) {
return origin.indexOf(item) === index;
}));
}, []);
var results = arrays.reduce(function(carry, item, index, arr) {
if(
// just select items, which have more then 1 occurance
arr.filter(function(fItem) {
return fItem === item;
}).length > 1 &&
// ... and which are not already in results
!~carry.indexOf(item)
) {
carry = [].concat(carry,item);
}
return carry;
}, []);
return results;
};
Here's a version that uses 2 reduces.
The first iterates the arrays only once to create a hashmap object to track instance counts, the second to return values where counts match number of arguments
function intersection(){
// convert arguments to array of arrays
var arrays = [].slice.call(arguments);
// create an object that tracks counts of instances and is type specific
// so numbers and strings would not be counted as same
var counts= arrays.reduce(function(a,c){
// iterate sub array and count element instances
c.forEach(function(val){
var propName = typeof val + '|' + val;
// if array value not previously encountered add a new property
a[propName] = a[propName] || {count:0, value: val};
// increment count for that property
a[propName].count++;
});
return a;
},{});
// iterate above object to return array of values where count matches total arrays length
return Object.keys(counts).reduce(function(resArr, propName){
if(counts[propName].count === arrays.length){
resArr.push(counts[propName].value);
}
return resArr;
},[]);
}
console.log(intersection([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]))
Could use some fine tuning to make sure there are enough arguments and that they are all arrays
Here's what I came up with using vanilla javascript and one call to reduce.
function intersection(){
var arrays = [].slice.call(arguments);
var first = arrays[0];
var rest = arrays.slice(1);
return first.reduce(function(all, item, index){
var push = rest.every(function(subArray){
return subArray.indexOf(item) > -1;
});
if(push){
all.push(item);
}
return all;
},[])
}
console.log(intersection([5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]));
function intersection(arrays) {
let common = arrays.reduce(function(accumulator, currentValue) {
return accumulator.filter(function(x){
return currentValue.indexOf(x) > -1;
})
})
return common;
}
To optimize your answer that couldn't work on more than 2 subarrays and didn't use reduce, here's the code that works for however many subarrays you pass in.
function intersection(arr1, arr2, arr3){
let ans = arr1[0]; // ans = [5,10,15,20]
for(let i = 0; i < ans.length; i++){ // i = 0...3
for(let j = 1; j < arr1.length; j++){ // j = 1...2
if(!(arr1[j].includes(ans[i]))){ // if the new subarray doesn't include an element in the ans
ans.splice(i, 1); // delete the element from ans
}
}
}
return ans;
}
const arr1 = [5, 10, 15, 20];
const arr2 = [15, 88, 1, 5, 7];
const arr3 = [1, 10, 15, 5, 20];
console.log(intersection([arr1, arr2, arr3])); // should log: [5, 15]

For-loop, map and forEach Javascript

I am trying to create a deck of 52 cards. I can create it easily with double for-loop but it has O(n2) complexity. So I was trying to play with map() and forEach() array methods but things are complex with them needing to return stuffs. Here is my code below.
(function deckCreate() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var suits = ["clubs", "diamonds", "hearts", "spades"];
var newDeck = values.map(function(xValue) {
suits.forEach(function(xSuit) {
return [xSuit,xValue];
});
});
return newDeck;
}());
It gives an array of length 13 all undefined inside. I tried swapping forEach() before map() just incase but the result was the same.
The issue I found while console.log() inside those functions was that the elements were not being mapped to each other but were printed all separately. What could be the issue be?
You're not returning anything from your map function, so the implicit return value is undefined, hence your array of 13 undefined values.
suits.forEach needs to be return suits.map. This will give you an array of 13 elements, where each element is an array of four elements, where each element of the inner array is a two element [suit, value] array. You can then reduce the top-level array into the 52 element array you're after:
var newDeck = values.map(function(xValue) {
return suits.map(function(xSuit) {
return [xSuit,xValue];
});
}).reduce(function (a, b) { return a.concat(b) });
The reason you're having trouble is that you aren't returning from your outer .map() callback. Even if you did, though, [].forEach always returns undefined, regardless of what happens inside its callback.
So, since you're using forEach to iterate the inner array, you get an array of 13 undefineds from map.
What you should be using is .map() all the way down and return every step of the way:
const first = [1, 2, 3];
const second = ['a', 'b', 'c'];
const mapped = first.map(function(digit) {
return second.map(function(letter) {
return [digit, letter];
});
});
console.log(mapped);
Seeing how you're clearly trying to learn and improve yourself, I'll leave you to adjust this example to your specific case.
P.S. If you want a flattened array, have a look at [].reduce() and [].concat().
Use a simple for loop. Use suits[Math.floor(i / 13)] to get the right suit, and use the remainder operator % to get the card number for each suit:
function deckCreate() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var suits = ["clubs", "diamonds", "hearts", "spades"];
var newDeck = [];
for (var i = 0; i < 52; i++) {
newDeck.push([suits[Math.floor(i / 13)], values[i % 13]]);
}
return newDeck;
}
var result = deckCreate();
console.log(result);
I think it's better to simplify it.
We know that there are just 4 suits, so it's enough to get list by suit name:
function createDeck() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var deck = {"clubs": values.slice(), "diamonds": values.slice(), "hearts": values.slice(), "spades": values.slice()};
return deck;
}
var deck = createDeck();
console.log('CLUBS:', deck.clubs);
console.log('DIAMONDS:', deck.diamonds);
console.log('HEARTS:', deck.hearts);
console.log('SPADES:', deck.spades);
P.S. in my case I'll create a class that makes generation, iteration and etc stuff to easily use it.
function Deck() {
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var suits = ['clubs', 'diamonds', 'spades', 'hearts'];
this.getSuits = function() {return suits.splice();}
this.getValues = function() {return values.splice();}
var asObject;
this.asObject = function() {
if(asObject) return asObject;
asObject = {};
suits.map(function(suit) {
asObject[suit] = values.slice();
});
return asObject;
};
var asArray;
this.asArray = function() {
if(asArray) return asArray;
asArray = [];
suits.map(function(suit) {
values.map(function(value) {
asArray.push([suit, value]);
});
});
return asArray;
}
this.iterate = function(fn) {
this.asArray().map(fn);
}
}
var deck = new Deck();
deck.iterate(function(card) {
console.log('CARD: ', card[0], card[1]);
});
console.log(deck.asObject());
console.log(deck.asArray());

Categories