There are arrays A and B. We need to add to array C all the values of arrays A and B that are equal in both value and indexes.
A = [a,b,c], B = [c,b,a], C = [b]
Also: to add to array D all unique values from array A that are contained in array B.
A = [d,a,b,c], B = [c,b,a,a], D = [a,b,c]
Is it possible to do this without a nested loop? I can handle the first task, but how do I fill D with values?
for (let i = 0; i < a.length; i++) {
if (b[i] === a[i]) {
c.push(a[i]);
} else if() {
// Some code
}
}
filter method? (for second question)
let D = A.filter(e => B.includes(e));
adding because you asked for D to contain unique values from A, thanks to #jarmod for pointing this out. You could use filter again to remove duplicates. I found this: Get all unique values in a JavaScript array (remove duplicates)
You can use a Set to keep track of unique values and .has() for efficient lookup in that Set. This code uses only one loop in your code, but several set operations use loops in their internal implementation (to build a set from an Array or convert a set to an Array). That cannot be avoided.
const A = ['e', 'd', 'a', 'b', 'c'];
const B = ['e', 'c', 'b', 'a', 'a'];
function processArrays(a, b) {
const c = [];
const aSet = new Set(a);
const commonSet = new Set();
for (let i = 0; i < a.length; i++) {
if (b[i] === a[i]) {
c.push(a[i]);
}
// if aSet has this value, then add it to commonSet
if (aSet.has(b[i])) {
commonSet.add(b[i])
}
}
return { samePositionElements: c, commonElements: Array.from(commonSet) };
}
console.log(processArrays(A, B));
Note, I don't do an if/else here because these are two independent output tests. The first is if the two elements in the same position have the same value. The second is whether it's an element in common with the other array (regardless of position).
This code assumes the two input arrays have the same length. If you wish to support inputs with different length, that can be accommodated with slightly more code.
Simple Way and Fast Solution:
let A = ['a','b','c', 'e'];
let B = ['c','b','a', 'e'];
let common =[];
for(let i=0; i<A.length && i<B.length; i++){
if(A[i]==B[i]){
common.push(A[i])
}
}
console.log(common)
Related
I wanted to make a simple game for my first project but I've encountered some problems with the logic behind it.
The game should compare two arrays, one of which stores user input and the other which is randomly generated. Both arrays have the length of n (let's say n=3) and accept n unique characters as their values. Let's say that the user input is ['A','A', 'B'] and that the winning combination is ['B', 'A', 'C']. The win condition is simple, all three elements from the user input array must be valid. An element is valid if both it's value and index correspond to the element in the second array.
Checking this is simple enough:
for (let i = 0; i<arr1.length; i++) {
for (let j = 0; j<arr1.length; j++){
if (arr[i] === arr1[j] && getIndices(arr[i], arr1[j]) === true){
valid ++;
}
However, I also want to keep track of misplaced elements, where arr[i] matches the value of arr[j] but the equality check on their indices returns false. Here's the problem, if I were to put this inside an else statement, and compare ['A', 'B', 'A'] to ['A', 'C', 'C'] it would return 1 valid as it should, but also 1 misplaced which is incorrect because 'A' only appears once in the second array. How would you set up the statement to avoid this?
I'm quite new to this so I haven't tried much.
If the input value and the win condition has the same length, you don't need two for loop. And name your variables correctly: inputs and condition.
var points = 0
var misplacedElements = []
for (let i = 0; i<inputs.length; i++) {
//findIndex returns index of the element on the condition array
//If element don't exist returns -1
const indexOfInput = condition.findIndex(e=> e === inputs[i])
if(indexOfInput != -1){
//Element contains but might not be on the same index
if(indexOfInput == i){
//On the same index so give a point
points++
}else{
//Hold the index or the element depends to your need
misplacedElements.push( i )
}
}
You can ask if you don't understand.
This is the JS way.
const userList = ['A', 'B', 'C'];
const winList = ['A', 'B', 'A'];
const scoreCalculator = ({ user, win }) => {
let points = 0;
user.forEach((value, index) => {
if (value === win[index]) {
points++;
}
});
return points;
}
console.log(scoreCalculator({user: userList, win: winList}));
The cost will be O(n).
With normal for execution.
const userList = ['A', 'B', 'C'];
const winList = ['A', 'B', 'A'];
const scoreCalculator = ({ user, win }) => {
let points = 0;
for(let i = 0; user.list; i++) {
if (user[i] === win[i]) {
points++;
}
});
return points;
}
console.log(scoreCalculator({user: userList, win: winList}));
As you can see, Array.prototype.forEach() its work like normal for.
I'm trying to solve a problem where you have an array
array = [[a,b,c],[e,f,g],[h,i,j]]
I want to loop through the first letter of the first array, then look at the first of all the other arrays then do a comparison whether they're equal or not. I'm unsure how to do this given that a loop will go through everything in its array. I wrote a nested loop below, but I know it's know it's not the right approach. I basically want something that looks
like
if (array[0][0] == array[1][0] == array[2][0]), then repeat for each element in array[0]
var firstWord = array[0];
var remainderWords = array.slice(1);
for(var i = 0; i < firstWord.length; i++){
for (var j = 0; i< remainderWords.length; j++){
if firstWord[i] == remaindersWord[j][i]
}
Any help would be appreciated.
Use Array.every() to check that a condition is true for all elements of an array.
firstWord.forEach((letter, i) => {
if (remainderWords.every(word => word[i] == letter)) {
// do something
}
});
You can use three nested for or .forEach loops to get to the items in the remainderItems array. Try this
var array = [['a', 'b', 'c'], ['b', 'f', 'g'], ['h', 'c', 'j']];
var firstWord = array[0];
var remainderWords = array.slice(1);
firstWord.forEach((l, idx) => {
remainderWords.forEach(arr => {
arr.forEach(v => {
if (l == v) {
// matched do whatever you want with the variable here
console.log(v)
}
})
})
})
Imagine we have two Arrays.
var a = [B, C, E];
var b = [A, B, C, D, E];
Array a is our default Array that we have saved locally.
Now the api we fetch from sends us the new version of this Array a called b.
How can I use lodash so I can get the difference of this two arrays so I get in this case
[A, D] returned ?
UPDATE:
I came up with my own solution it kinda worked, so only try this if the accepted answer does not work for some reason:
const getObjectDiff = (newArr, oldArr) => {
if (_.isEqual(newArr, oldArr)) {
return true;
} else {
const checkLength = newArr.length;
const checkerLength = oldArr.length;
var indexes = [];
var falseIndexes = [];
var allIndexes = [];
for (let i = 0; i <= checkerLength; i++) {
for (let j = 0; j <= checkLength - 1; j++) {
allIndexes.push(i);
if (_.isEqual(newArr[i], oldArr[j])) {
indexes.push(i);
} else {
falseIndexes.push(i);
}
}
}
const falseUniqueIndexes = _.uniq(falseIndexes);
return _.difference(falseUniqueIndexes, indexes);
}
};
```
We can use _.differenceWith along with the _.isEqual to check deep equality between objects.
_.isEqual will perform a deep comparison between two objects to see if they are equivalent so we can use this to compare, say, objects that have been decoded from JSON.
This is a simple example, however, this will work with much more complex objects as long as they are equal (as determined by lodash)
const apiResponseA = '[{"id":"B"},{"id":"C"},{"id":"E"}]';
const apiResponseB = '[{"id":"A"},{"id":"B"},{"id":"C"},{"id":"D"},{"id":"E"}]'
const a = JSON.parse(apiResponseA);
const b = JSON.parse(apiResponseB);
console.log("a:", apiResponseA)
console.log("b:", apiResponseB)
console.log("Difference:", JSON.stringify(_.differenceWith(b,a, _.isEqual)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>
I realize you asked specifically for a lodash solution, but I just want to mention that you could do this pretty simply without a 3rd party library - just use Array.prototype.filter and Array.prototype.includes, like this:
var a = ['B', 'C', 'E'];
var b = ['A', 'B', 'C', 'D', 'E'];
function getDifference(a, b) {
return b.filter(letter => !a.includes(letter));
}
console.log(getDifference(a, b));
Please note: the linked question, "How can I create every combination possible for the contents of two arrays?" does not solve this particular question. The persons that labeled that did not fully understand this specific permutation and request.
If you have two arrays (arr1, arr2) with n elements in each array (i.e., each array will be the same length), then the question is: What's the best method to get/determine all the possible matches where elements do not match with other elements in the same array and where order does not matter?
For example, let's say I have:
arr1 = ["A","B","C"];
arr2 = ["Z","Y","X"];
I would like to get back an array of arrays where each element of one array is paired with an element of another array. So the result would be a unique set of arrays:
matches = [
[["A","Z"],["B","Y"],["C","X"]],
[["A","Z"],["B","X"],["C","Y"]],
[["A","Y"],["B","X"],["C","Z"]],
[["A","Y"],["B","Z"],["C","X"]],
[["A","X"],["B","Z"],["C","Y"]],
[["A","X"],["B","Y"],["C","Z"]],
]
Please note, these two arrays would be the same:
[["A","Z"],["B","Y"],["C","X"]]
[["B","Y"],["C","X"],["A","Z"]]
I am trying to do this with vanilla JavaScript but am completely open to using Lodash as well. For an added bonus, since this can get out of control, speed and performance are important. But right now, I am just trying to get something that would yield a proper result set. To limit this, this function would probably not be used with more than two arrays of 50 elements each.
Here is my latest attempt (using lodash):
function getMatches(arr1, arr2){
var matches = [];
for (var arr1i = 0, arr1l = arr1.length; arr1i < arr1l; arr1i++) {
for (var arr2i = 0, arr2l = arr2.length; arr2i < arr2l; arr2i++) {
matches.push(_(arr1).zip(arr2).value());
arr2.push(arr2.shift());
}
}
return matches;
}
[[A, 1], [B, 2]]
is the same as
[[B, 2], [A, 1]]
in your case, which means that the solution depends on what you pair to the first elements of your array. You can pair n different elements as second elements to the first one, then n - 1 different elements as second elements to the second one and so on, so you have n! possibilities, which is the number of possible permutations.
So, if you change the order of the array elements but they are the same pair, they are equivalent, so you could view the first elements as a fixed ordered set of items and the second elements as the items to permutate.
Having arr1 = [a1, ..., an] and arr2 = [b1, ..., bn] we can avoid changing the order of a1. So, you permutate the inner elements and treat the outer elements' order as invariant, like:
const permutations = function*(elements) {
if (elements.length === 1) {
yield elements;
} else {
let [first, ...rest] = elements;
for (let perm of permutations(rest)) {
for (let i = 0; i < elements.length; i++) {
let start = perm.slice(0, i);
let rest = perm.slice(i);
yield [...start, first, ...rest];
}
}
}
}
var other = ['A', 'B', 'C'];
var myPermutations = permutations(['X', 'Y', 'Z']);
var done = false;
while (!done) {
var next = myPermutations.next();
if (!(done = next.done)) {
var output = [];
for (var i = 0; i < next.value.length; i++) output.push([other[i], next.value[i]]);
console.log(output);
}
}
You're just looking for permutations. The first elements of your tuples are always the same, the second ones are permuted so that you get all distinct sets of combinations.
const arr1 = ["A","B","C"];
const arr2 = ["Z","Y","X"];
const result = permutate(arr2).map(permutation =>
permutation.map((el, i) => [arr1[i], el])
);
This implementation uses Typescript and Lodash.
const permutations = <T>(arr: T[]): T[][] => {
if (arr.length <= 2)
return arr.length === 2 ? [arr, [arr[1], arr[0]]] : [arr];
return reduce(
arr,
(acc, val, i) =>
concat(
acc,
map(
permutations([...slice(arr, 0, i), ...slice(arr, i + 1, arr.length)]),
vals => [val, ...vals]
)
),
[] as T[][]
);
};
I want to make the next array based on another 2 arrays:
array1 = ['a', 'b']
array2 = [1,2,3]
I want to create the next array
newArray = [['a',1], ['a',2], ['a',3], ['b',1], ['b',2], ['b',3]]
Here is my code:
var test1 = ['a', 'b'];
var test2 = [1,2,3], arr1 = [], arr2 = [];
for(var i = 0; i < test1.length; i++){
arr1 = [];
arr1.push(test1[i]);
for(var x = 0; x < test2.length; x++){
if(arr1.length > 1)
arr1.pop();
arr1.push(test2[x])
arr2.push(arr1);
}
}
console.log("arr1:",JSON.stringify(arr1), "arr2:" ,JSON.stringify(arr2));
But it returns the last element of the second array.
[['a',3], ['a',3], ['a',3], ['b',3], ['b',3], ['b',3]]
Why is this happening?
Every other answer about array lengths, and similar things are not right. The only reason you get 3 (or whatever the last value/length is) all over the place is because Arrays are by reference, and functions, not for-loops create lexical scope. This is one of the reasons you hear that 'functions are first-class citizens in javascript'. This is a classical example, and frequently in interviews too, used to trip up devs who are not used to how scoping in javascript really behaves. There are some ways to fix it that involve wrapping the innards of loops in functional scopes, and passing in the index, but I'd like to suggest a more 'javascript centric' approach, and that is to solve the problem with functions.
See this example (which by the way is also a clear way to implement your goal.)
var test1 = ['a', 'b'];
var test2 = [1,2,3];
// this will iterate over array 1 and return
// [[ [a,1],[a,2],[a,3] ],[ [b,1],[b,2],[b,3] ]]
var merged = test1.map(function(item1){
return test2.map(function(item2){
return [item1, item2];
});
});
//this is a slick way to 'flatten' the result set
var answer = [].concat.apply([],merged )
console.log(answer) //this is it.
Functions () make scope - not 'brackets' {}. The easiest fix is usually to use functions to solve your problems as they create lexical scope. The most trusted library on npm, lodash, for instance is based on this. I think you'll write less and less loops from day to day js as you progress, and use more functions.
Working example on js fiddle:
https://jsfiddle.net/3z0hh12y/1/
You can read more about scopes and closures here
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch1.md
And one more thing: when you think you want a loop in js, you usually want Array.map(), especially if you're remapping values.
You problem lies in the second loop. because you have a single reference to ary1 you are over writing the value with each loop.
so first time it loops your array will have
[['a',1]]
but because you you only have one reference to ary1 you are just editing the value you just pushed into ary2.
so then you get this:
[['a',2],['a',2]]
you may think that you are pushing a new array in but it is in fact the same exact array! so the pattern continues and you get the result that you are seeing.
It's occurring because you're popping elements off arr1 once it's length exceeds 1, so the last element is all that persists.
Try this:
var test1 = ['a', 'b'];
var test2 = [1,2,3];
var arr1 = [], arr2 = [];
for(var i = 0; i < test1.length; i++) {
for(var x = 0; x < test2.length; x++) {
arr1[arr1.length] = [test1[i], test2[x]];
}
}
console.log(JSON.stringify(arr1));
Beside the solutions for fixed style for two array, you might use another approach to get the result. you could use an iterative and recursive approach for variable length of parts an their length.
function combine(array) {
function c(part, index) {
array[index].forEach(function (a) {
var p = part.concat([a]);
if (p.length === array.length) {
r.push(p);
return;
}
c(p, index + 1);
});
}
var r = [];
c([], 0);
return r;
}
console.log(combine([['a', 'b'], [1, 2, 3]]));
console.log(combine([['a', 'b', 'c'], ['1', '2', '3', '4'], ['A', 'B']]));
console.log(combine([['a', 'b', 'c'], ['1', '2', '3', '4'], [['A'], ['B']]]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I guess the proper functional approach would be done by a single liner reduce and nested map duo.
var arr = ['a', 'b'],
brr = [1,2,3],
result = arr.reduce((p,c) => p.concat(brr.map(e => [c,e])),[]);
console.log(JSON.stringify(result));
your approach is a bit off, you better take this approach (which is pretty simple I think): DEMO
var array1 = ['a', 'b'],
array2 = [1,2,3],
nextArray=[];
for(var i=0, array1Length=array1.length; i < array1Length; i++){
for(var x=0, array2Length=array2.length; x < array2Length; x++){
nextArray.push([array1[i],array2[x]]);
}
}
console.log(nextArray);
Yeah, that's way more complicated than it needs to be. The result you want is called the Cartesian product, and can be generated like this:
const cartesianProduct = (a, b) => {
const rv = [];
a.map(ae => b.map(be => rv.push([ae, be])));
return rv;
};
const array1 = ['a', 'b']
const array2 = [1,2,3]
console.log(JSON.stringify(cartesianProduct(array1, array2)));
If Javascript6 had flatMap it would be even simpler:
const cartesianProduct = (a, b) =>
a.flatMap(ae => b.map(be => [ae, be]));
I modified your code a little (but not too much, I tried to keep the same approach). The solution was to create a new array (arr1) in the inner loop. Otherwise, the first correct pair ['a', 1] would get overridden just after begin pushed to arr2 at the next loop pass.
var test1 = ['a', 'b'];
var test2 = [1,2,3];
var arr2 = [];
for(var i = 0; i < test1.length; i++){
for(var x = 0; x < test2.length; x++){
var arr1 = [];
arr1.push(test1[i]);
if(arr1.length > 1){
arr1.pop();
}
arr1.push(test2[x]);
arr2.push(arr1);
}
}