I have the following array:
var array = [
[1, 2, 3, 4, 5],
[2, 3],
[3, 4],
[3]
];
I'm trying to end up with a unique set of numbers from the arrays that appear in all arrays.
Therefore in this case returning
[3]
Any suggestions?
Many thanks :)
Store the value of array[0] in a variable (let's call it result).
Loop from array[1] to the end.
In this loop, run through all the values of result. If the current value of result is not in array[x] remove this value from result.
At the end of the loop, result only contains the desired values.
Aside from the obvious "iterate over every array and find matching numbers in every other array" you could flatten (concat) the original array, sort it, then look for numbers that occur at four consecutive indexes. I'm not a fan of questions where OP doesn't show any effort, but this was quite fun, so here it goes
array.reduce(function(prev, cur){
return prev.concat(cur);
})
.sort()
.filter(function(item, i, arr){
return arr[ i + array.length - 1 ] === item;
});
Or ES2015:
array.reduce((prev, cur)=>prev.concat(cur))
.sort()
.filter((i, idx, arr)=>(arr[idx+array.length-1]===i));
After learning i was using the wrong javascript method to remove from an array (pop) and some more tinkering. I got it working many thanks for those who responded.
var array = [
[2, 3, 5, 1],
[3, 4, 2, 1],
[3, 2],
[3, 4, 2, 5]
];
var result = array[0]
for (var i = 1; i < array.length; i++) {
for (var j = 0; j < result.length; j++) {
var arrayQuery = $.inArray(result[j], array[i]);
if(arrayQuery == -1){
result.splice(j, 1)
}
};
};
Try this:
var array = [
[1, 2, 3, 4, 5],
[2, 3],
[3, 4],
[3]
];
var arr = [];
for(var x in array){
for(var y in array[x]){
if(arr.indexOf(array[x][y]) === -1){
arr.push(array[x][y]);
}
}
}
console.log(arr);
Output:
[1, 2, 3, 4, 5]
Working Demo
Related
Let's say I have 4 arrays:
var arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
And I want to return the child/sub arrays that start with both 1 and 2, what type of loop would I need?
Currently, this is what I have:
var arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
var selected = [1, 2]; // These are the values that need to match
var result = [];
for (var i = 0; i < selected.length; i++) {
for (var j = 0; j < arrays.length; j++) {
if (arrays[i][j] === selected[i]) {
result.push(arrays[i]);
}
}
}
When there's more than 1 value in the selected array, it seems to return all the ones that match 2 on the second index, so the result would be:
[
[1, 2, 1],
[1, 2, 3],
[0, 2, 2]
]
The loop needs to ensure that on the second iteration it's making sure the first value is still true, as my intended result would be:
[
[1, 2, 1],
[1, 2, 3]
]
Please someone help me, I've had my head trying hundreds of different loop and checks variations for 2-3 days.
Thanks so much!!
Jake
Your current code pushes to the result array whenever any given index matches between arrays and selected. Instead you will need to reverse your loops and iterate over selected for every sub array and check if every element matches, if not break the inner loop and don't push.
const arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2],
];
const selected = [1, 2]; // These are the values that need to match
const result = [];
for (let i = 0; i < arrays.length; i++) {
let match = true;
for (let j = 0; j < selected.length; j++) {
if (arrays[i][j] !== selected[j]) {
match = false;
break;
}
}
if (match) {
result.push(arrays[i]);
}
}
console.log(result);
A more modern solution would be to use filter() with a nested every() call on selected.
const arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2],
];
var selected = [1, 2];
const result = arrays.filter(arr => selected.every((n, i) => n === arr[i]));
console.log(result);
Here is another approach where you turn both arrays to string and check it those inner arrays start with selected array.
var arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
var selected = [1, 2];
const result = arrays.filter(e => e.toString().startsWith(selected.toString()))
console.log(result)
Let's try to put your condition into words. That way, an implementation may come to mind more easily.
A short wording may be: "Take all arrays that match (rather: start with) a certain sub-array." In code, it may look like this:
const arrays = [
[1, 2, 1],
[1, 3, 4],
[1, 2, 3],
[0, 2, 2]
];
const selection = [1, 2];
const result = filterArrays(arrays, selection);
console.log(result);
function filterArrays(arrays, selection) {
const selectedArrays = [];
for (let i = 0; i < arrays.length; ++i) {
const array = arrays[i];
const subarray = array.slice(0, selection.length); // Get starting sub-array
if (compareArrays(subarray, selection)) {
selectedArrays.push(array);
}
}
return selectedArrays;
}
/*Ignore; helper function*/
function compareArrays(array1, array2) {
if (array1.length !== array2.length) return false;
const length = array1.length;
for (let i = 0; i < length; ++i) {
if (array1[i] !== array2[i]) return false;
}
return true;
}
.as-console-wrapper {max-height:100%!important}
Another, more specific wording may be: "Take all arrays that match a selection at an index." Note that we only reworded the "match a sub-array" part. I believe this is what you tried.
Refer to pilchard's answer for an implementation. Note that their implementation assumes the arrays in arrays to be at least the same length as selected.
I see you used var instead of the preferred modern let/const declarators. Here's a short outline of their differences:
let/const declarators:
Block-scoped.
Narrower scope means less name-space pollution.
More similar to declarators in other well-known languages:
Variables of these declarators cannot be used before their declaration (see TDZ).
var declarator:
Function-scoped.
Hoisted and with no TDZ, resulting in this (perhaps confusing) behaviour:
Variables declared with var can be used even before their declaration.
Duplicate declarations are allowed since they are effectively the same.
Also, JavaScript has different kinds of for-loops:
for-loop: The for-loops you used are this kind. It is the most versatile kind.
for...of-loop: A loop to iterate over an iterable object (see iterators). For example, arrays are iterable, so you can get its values with a for...of-loop:
const values = [1, 2, 3];
let sum = 0;
for (const value of array) {
sum += value;
}
console.log(sum); // -> 6
for...in-loop: A loop to iterate over enumerable properties of an object. It is easily confused with a for...of-loop, but MDN's example demonstrates the differences understandably.
In my code example above, the for-loop in filterArrays() can be replaced with a for...of-loop to better convey my intention: To iterate over all arrays in arrays, disregarding their index:
for (let i = 0; i < arrays.length; ++i) {
const array = arrays[i];
// ...
}
// Same as
for (const array of arrays) {
// ...
}
I have an array with multiple arrays inside. I would like to multiply the contents of each array.
Here is my code--currently not getting anything in console.
The desired output is 3 numbers-- the result of multiplication of the three arrays
var arr = [
[1, 2, 3],
[3, 4],
[7, 8, 9]
]
var mult = 1
for (i = 0; i < arr.length; i++) {
for (j = 0; j < arr.length; j++) {
mult *= arr[i][j]
}
console.log(mult)
}
You could map the result of the nested multiplying.
const
multiply = (a, b) => a * b,
array = [[1, 2, 3], [3, 4], [7, 8, 9]],
result = array.map(a => a.reduce(multiply, 1));
console.log(result);
You can try this, it is easier using map and reduce functions. arr.map(array => array.reduce(arrayMult, 1)) is the multiplication of each inner array, like [ 6, 12, 504 ], and the last reduce is to multiply these values.
var arr = [[1, 2, 3],[3, 4],[7, 8, 9]];
const arrayMult = (prev, curr) => prev * curr;
const total = arr.map(array => array.reduce(arrayMult, 1)).reduce(arrayMult, 1);
You should iterate over rows. So you need to change inner loop on this :
for (j = 0; j < arr[i].length; j++)
const array = [
[1, 2, 3],
[3, 4],
[7, 8, 9]
];
for (i = 0; i < array.length; i++) {
let result = 1;
for (j = 0; j < array[i].length; j++) {
result *= array[i][j]
}
console.log(result);
}
Nothing wrong with good old iterating; your existing approach only had one bug, on the inner loop you were still checking the length of the top-level array instead of the inner array; j should range from 0 to arr[i].length, not arr.length.
An alternative approach uses map and reduce; comments below explain how it works:
var arr = [
[1, 2, 3],
[3, 4],
[7, 8, 9]
]
console.log(
arr.map(a => { // <-- calls a function on each element of arr,
// leaving the results in an array
return a.reduce( // <-- reducer is like map, calling a function
// on each element of a, but collects its results
// in a "collector" variable passed to each step
(val, sum) => {return val * sum}, // <-- multiply this value with
// the ones so far
1 // <-- initial value for the collector
)
})
)
One approach was to iterate the array of arrays via map which for each array puts the product of all of its items where the latter gets achieved via a multiplying reduce task.
console.log([
[1, 2, 3],
[3, 4],
[7, 8, 9],
].map(arr =>
arr.reduce((x, y) => x * y)
));
Im trying to copy elements of arr1 into arr2 at index n. The elements must be copied in the exact order they're in. I can get the code to work when I loop through the arrow backwards but I cant pass the tests because its not in order.
function frankenSplice(arr1, arr2, n) {
let newArr = arr2.splice(" ");
for(let i = 0; i < arr1.length;i++) {
newArr.splice(n,0,arr1[i]);
}
return console.log(newArr);
}
An example of how this should be called is frankenSplice([1, 2, 3], [4, 5, 6], 1);
Expected output is [4, 1, 2, 3, 5]
I keep getting [ 4, 1, 2, 3, 5, 6 ]
The reason your output is coming backwards is because you keep inserting at the same position n. This pushes the previous element after it, so they end up in reverse order. If you increment n each time through the loop, you'll insert them in order.
But there's no need for a loop, use spread syntax to use all of arr1 as the arguments in a single call to splice().
function frankenSplice(arr1, arr2, n) {
let newArr = [...arr2]; // make copy of arr2
newArr.splice(n, 0, ...arr1); // splice arr1 into the copy
return newArr;
}
console.log(frankenSplice([1, 2, 3], [4, 5, 6], 1));
I don't understand why you don't expect 6 in the output.
Not sure why the 6 is getting deleted but maybe something like this:
function frankenSplice(arr1, arr2, n) {
let newArr = arr2
newArr.splice(n+1)
for(let i = arr1.length - 1; i >= 0; i--) {
newArr.splice(n,0,arr1[i]);
}
return console.log(newArr);
}
frankenSplice([1, 2, 3], [4, 5, 6], 1);
I'm on my third week of learning Javascript and got an assignment that's giving me some trouble.
I'm supposed to create a function called mix that takes two parameters that are two arrays. When called the function should return a new list which alternates between the two previous arrays (see example below).
This is about arrays and loops so I need to use those. also, I'm only allowed to use the array functions: push, pop, shift & unshift.
My teacher said that this is solved the easiest using a while loop.
Example
mix([], []) === []
mix([2, 4], []) === [2, 4]
mix([], [8, 16]) === [8, 16]
mix([1, 3, 5], [2, 4]) === [1, 2, 3, 4, 5]
mix([10, 9], ['a', 'b', 'c']) === [10, 'a', 9, 'b', 'c']
Before I got the tip about the easiest being a while loop I started trying with a for a loop. The problem I'm having here is that it works as long as the arrays are the same length, but I'm having trouble understanding how I can solve it so the arrays can have different lengths.
Since I'm trying to learn I want pointers in the right direction and not the whole answer!
Please excuse my chaotic beginner code :)
My current code
function mix(array1, array2) {
let newList = [];
for(i = 0; i < array1.length || i < array2.length; i++) {
if(array1.length > 0 || array2.length > 0){
newList.push( array1[i] );
newList.push( array2[i] );
}
}
return newList;
}
mix([10, 9],['a', 'b', 'c'])
I would also like a pointer for how a while loop would be easier and how i would go about using that instead.
Thanks in advance!
To fix your current code, you need to separately check whether i < array1.length (and if so, push array1[i]), and also do the same sort of test for array2:
function mix(array1, array2) {
let newList = [];
for (let i = 0; i < array1.length || i < array2.length; i++) {
if (i < array1.length) {
newList.push(array1[i]);
}
if (i < array2.length) {
newList.push(array2[i]);
}
}
return newList;
}
console.log(mix([10, 9], ['a', 'b', 'c']));
Make sure to declare the i with let i, else you'll implicitly create a global variable (or throw an error in strict mode).
To do this with a while loop, I'd loop while either array has a length, and shift (remove the [0]th item) from them:
function mix(array1, array2) {
const newList = [];
while (array1.length || array2.length) {
if (array1.length) {
newList.push(array1.shift());
}
if (array2.length) {
newList.push(array2.shift());
}
}
return newList;
}
console.log(mix([10, 9], ['a', 'b', 'c']));
You can do much better with array shift, it takes first element from array and returns its value, so for example
const firstElement = [1, 2, 4].shift();
// firstElement - 1
// array [2, 4]
with this info you can now write your function like so:
function (arr1, arr2) {
const resultArr = [];
while(arr1.length && arr2.length) {
resultArr.push(arr1.shift());
resultArr.push(arr2.shift());
}
return resultArr.concat(arr1, arr2);
}
You can achieve it using Array.prototype.shift(), Array.prototype.push() and Spread syntax
function mix(arr1,arr2) {
var newArr=[];
while(arr1.length>0&&arr2.length>0) {
newArr.push(arr1.shift(),arr2.shift());
}
newArr.push(...arr1,...arr2);
return newArr;
}
An alternative approach could be to consider the input arrays as a two-dimensional array.
You can then:
rotate/transpose the two-dimensional array (rows become columns); and
flatten the result (rows are concatenated into a one-dimensional array).
The transformation looks like this for the example input [1, 3, 5], [2, 4]:
Rotate Flatten
[1, 3, 5], ⇒ [1, 2], ⇒ [1, 2, 3, 4, 5]
[2, 4] [3, 4],
[5]
Or, in code:
const mix = (...arrays) => {
const transposed = arrays.reduce((result, row) => {
row.forEach((value, i) => result[i] = [...result[i] || [], value]);
return result;
}, []);
return transposed.flat();
};
console.log(mix([], [])); // === []
console.log(mix([2, 4], [])); // === [2, 4]
console.log(mix([], [8, 16])); // === [8, 16]
console.log(mix([1, 3, 5], [2, 4])); // === [1, 2, 3, 4, 5]
console.log(mix([10, 9], ['a', 'b', 'c'])); // === [10, 'a', 9, 'b', 'c']
Benefits of this approach are that it automatically scales to allow more than two input arrays, and, unlike the shift operations, does not mutate the input arrays.
I would like to compare multiple array and finally have an array that contain all unique values from different array. I tried to:
1,Use the filter method to compare the difference between 2 arrays
2,Call a for loop to input the arrays into the filter method
and the code is as follows
function diffArray(arr1, arr2) {
function filterfunction (arr1, arr2) {
return arr1.filter(function(item) {
return arr2.indexOf(item) === -1;
});
}
return filterfunction (arr1,arr2).concat(filterfunction(arr2,arr1));
}
function extractArray() {
var args = Array.prototype.slice.call(arguments);
for (var i =0; i < args.length; i++) {
diffArray(args[i],args[i+1]);
}
}
extractArray([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3]);
However it does not work and return the error message "Cannot read property 'indexOf' of underfined" .... What's wrong with the logic and what should I change to make it works?
Many thanks for your help in advance!
Re: For all that mark this issue as duplicated ... what I am looking for, is a solution that can let me to put as many arrays as I want for input and reduce all the difference (e.g input 10000 arrays and return 1 array for unique value), but not only comparing 2 arrays .. The solutions that I have seen are always with 2 arrays only.
I don't use filters or anything of the sort but it will get the job done. I first create an empty array and concat the next array to it. Then I pass it to delete the duplicates and return the newly "filtered" array back for use.
function deleteDuplicates(a){
for(var i = a.length - 1; i >= 0; i--){
if(a.indexOf(a[i]) !== i){
a.splice(i, 1);
}
}
return a;
}
function extractArray() {
var args = Array.prototype.slice.call(arguments), arr = [];
for (var i = 0; i < args.length; i++) {
arr = deleteDuplicates(arr.concat(args[i]));
}
return arr;
}
var arr = extractArray([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3]);
console.log(arr) //[3, 2, 5, 1, 7, 4, 6]