Merging arrays with passing a function as parameter - javascript

I've been on this problem for about 3 hours and I can't figure this out. I've gone through Codecademy and Code School Javascript courses, and according to the blog that hosted this code block, I should know the answer:
var merge = function(array1, array2, callback){
//your code here.
}
var x = merge([1, 2, 3, 4], [5, 6, 7, 8], function(a, b){
return a + b;
});
//x should now equal [6, 8, 10, 12].
UPDATED WITH CORRECT CODE (Thank you so much T.J.!):
var merge = function(array1, array2, callback) {
var i, newArray = [];
for (i=0; i < array1.length; i++) {
newArray[i] = callback(array1[i], array2[i]);
}
return newArray;
};
var x = merge([1, 2, 3, 4], [5, 6, 7, 8], function(a, b){
return a + b;
});
console.log(x);

In merge, you:
Create a new, blank array for the return value.
Consider what you want to do if array1 and array2 aren't the same length, although they are in the example usage.
Use an index variable to loop from 0 through < array1.length (most likely a for loop).
Fill in the entry for each index in your return value array by calling callback, passing the entry for that index from array1 and from array2 in as arguments and storing its return value in your array.
Return the new array.

Related

Is it possible to use array.splice(argument[i], x) to edit an array for variables that meet a condition?

Key Question
-How do you use splice(argument[i], x)? Can it be used this way or am I only allowed to use numbers? ie (1, 2), (3, 0)
-I'm a little unsure of when element[i] can be used when an array is declared. So it can be used for both for loops and while loops when setting conditions? Can it be used as an argument or parameter in functions or additional methods besides splice?
What I want to do
-Write a function called "isEven".
-Given an array of numbers, "isEven" returns a new array.
-Only even numbers are outputted from the input array.
ex.
var output = isEven([1, 4, 5, 6, 10, 13]);
console.log(output); // --> [4, 6, 10]
Approach
-declare var digits to "catch" the array input.
-declare var NewArray for return of output array,
-use if condition to go through var digits and splice the variable at any given index.
-declare NewArray to the newly spliced array
function isEven(num) {
var digits = num;
var newArray = [];
digits.forEach(function(num) {
if (num[i] % 2 > 0) {
newArray = digits.splice(num[i], 1);
}
}) return newArray;
}
var ledoit = isEven([1, 4, 6]);
console.log(ledoit);
Try this:
function isEven(myArray) {
return myArray.filter(item => {
return Number.isInteger(item / 2)
})
}
Then isEven([1, 4, 5, 6, 10, 13]) will output [4,6,10]
You want to use the % operator:
var nums = [1, 4, 5, 6, 10, 13];
function getEvens(array){
for(var i=0,n,a=[],l=array.length; i<l; i++){
n = array[i];
if(n % 2 === 0)a.push(n);
}
return a;
}
console.log(getEvens(nums));
Albeit, not backward compatible, you could also do:
var nums = [1, 4, 5, 6, 10, 13];
function getEvens(array){
return array.filter(n => (n % 2 === 0));
}
console.log(getEvens(nums));

Multi-dimensional For Loop

I have three arrays and I need to create a set of rules based on these three arrays, but I'm struggling with the logic of how to write a function that will give me every possible combination of every entry in each array. So, I have, for example:
var array 1 = [1, 2];
var array 2 = [3, 4, 5];
var array 4 = [6, 7, 8, 9, 10];
And I'd wan't get back a string, object etc of all possible combinations (which I wont attempt to work out here). So for example:
var result = ["1-3-6", "2-3-6", "1,4,6"];
And so on, so far I've tried sitting down and composing a For Loop but I'm just really not sure where to start. I also looked at maps, but could not find any examples that went this deep, so I wasn't sure if a map would get the job done either.
The actual data I want to load in, the first array has 2 entries, the second have 7 and the last one had 6, so for the workings out I've done there should be 84 entries. That was based on (Array 3 * Array 2) * Array 1.
Hope that all makes sense I know it's a bit confusing. Also worth mentioning that I'm using Angular JS so an angular solution or vanilla JS solution is preferred but not essential.
What you are looking is the Cartesian product of arrays. You can use a function like this (extracted from here):
function cartesian() {
var r = [], arg = arguments, max = arg.length-1;
function helper(arr, i) {
for (var j=0, l=arg[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(arg[i][j]);
if (i==max)
r.push(a);
else
helper(a, i+1);
}
}
helper([], 0);
return r;
}
There are lot of examples, like:
JavaScript - Generating combinations from n arrays with m elements
With recursive:
Finding All Combinations of JavaScript array values
Cartesian product of multiple arrays in JavaScript
And with multiple (N) arrays:
Combine 2 arrays into 1 in all possible ways in JavaScript
Hope it helps!
Nested for loops will do
function arrComb(arr1, arr2, arr3) {
var l1 = arr1.length,
l2 = arr2.length,
l3 = arr3.length,
i, j, k, res = [];
for (i = 0; i < l1; i++) {
for (j = 0; j < l2; j++) {
for (k = 0; k < l3; k++) {
res.push(arr1[i] + '-' + arr2[j] + '-' + arr3[k]);
}
}
}
console.log(res)
}
arrComb([1, 2], [3, 4, 5], [6, 7, 8, 9, 10]);
A bit more elegant:
var array_1 = [1, 2];
var array_2 = [3, 4, 5];
var array_4 = [6, 7, 8, 9, 10];
var result = [];
for (var a1 of array_1)
{
for (var a2 of array_2)
{
for (var a3 of array_4)
{
result.push("\""+a1+"-"+a2+"-"+a3+"\"")
}
}
}
alert("["+result+"]")

Move every other value from array into a new array

I have two one-dimensional arrays, a and b. a has values and b is empty. The length of a is an even number. I'd like to remove every other value from a and move them to b, in the same order as they were placed in a.
var a = [1, 2, 3, 4, 5, 6], b = [];
becomes
var a = [1, 3, 5], b = [2, 4, 6];
I figured that filter would do the trick but I'm not that happy with the performance of it since the average length of a is 300-400.
b = a.filter((i, idx) => {
return idx % 2 == 0;
});
a = a.filter((i, idx) => {
return idx % 2 == 1;
});
I've also been looking at lodash to see if that library had anything that might help me and the only function that's near what I'm looking for is _.chunk(array, \[size=1\]).
I appreciate any and all help to help me figure out a better, faster way to do this.
Since you mentioned lodash you could do this with _.partition:
let a = [1, 2, 3, 4, 5, 6];
let b = [];
let i = -1;
[a, b] = _.partition(a, (item) => i++ % 2);
console.log(a);
console.log(b);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Partition's predicate is the identity function, which doesn't include the index of the item, so this comes with a compromise of an external index i.
Of course, you could always wrap this functionality into it's own function:
const splitEvenOdd = (array, i = -1) => _.partition(array, (item) => i++ % 2);
let a = [1, 2, 3, 4, 5, 6];
let b = [];
[a, b] = splitEvenOdd(a);
console.log(a);
console.log(b);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Vanilla JS ES5, simple and clean.
var a = [1, 2, 3, 4, 5, 6], b = [];
for(var i = a.length-1; i >= 0; i--) {
if(i % 2 === 1) {
b.unshift(a.splice(i, 1)[0])
}
}
Basically, it is iterating through a backwards, and if the condition is true splicing the item und adding it as first item of b.
To loop through the source once, the values can be added to a specific array depending on the index. For example:
const source = [1, 2, 3, 4, 5, 6];
let arrs = [[],[]];
for(let i = 0; i< source.length; i++)
arrs[i%2].push(source[i]);
let [a,b] = arrs;
console.log(a);
console.log(b);
Alternatively, if it's important to alter the original arrays, a can be filled in a direct iteration, since the index being processed is always ahead of the one being filled:
let a = [1, 2, 3, 4, 5, 6], b= [];
for(let i = 0; i< a.length; i++)
(i % 2 ? b : a)[Math.floor(i/2)] = a[i];
a.splice(a.length/2);
console.log(a);
console.log(b);
The best performance you can get for this is 0(n) or linear time since you have to iterate through the entire array. What may help is reducing the number of loops
var a=[];
var b=[];
function splitArray(arr)
{
for (var i=0;i<arr.length;++i)
{
if (arr[i]%2 == 0)
b.push(arr[i]);
else
a.push(arr[i]);
}
}
What this does is reduces the number of times you have to iterate through the original array from 2 to 1

Can i sort nested array using nested sort()?

This should be the input array
var a = [2,1,3,4,1,[4,6,2,4],2,4,1];
For the output i have two cases :- (index of internal array is not changing)
a = [1,1,2,3,4,[2,4,4,6],1,2,4]
and
a = [1,1,1,2,2,[2,4,4,6],3,4,4]
This is what i am trying to use :-
a.sort(function(a,b){
if(b instanceof Array){
b.sort();
}
})
Array.sort() is not built to handle partial Arrays, what you would need in your case, but we can work around this problem by pre-processing the data (wrapping it with additional information), then sorting and at the end, extracting the original values:
case 1: sorting the parts between the Arrays
[2,1,3,4,1,[4,6,2,4],2,4,1] -> [1,1,2,3,4,[2,4,4,6],1,2,4]
function sort1(arr){
//I add an artificial "property" of to the values, to "describe" the groups, and to be able to sort by
//each Array is it's own group (so they stay in order), and the values in between share the same group
var group = 0,
isArray = false;
//an intermediate Array holding all the information (in order) to either apply it to the current Array, or to return (map) it as a new Array
var intermediate = arr.map(function(v,i){
//last value was an Array, this is the first value after an Array, start a new group
if(isArray) ++group;
if(isArray = Array.isArray(v)){ //update isArray
v = sort1(v); //recursive sorting
++group; //the last group just ended here
}
//return a composition, that contains all the data I need to sort by
return {
group: group,
value: v
}
}).sort(function(a, b){
//forst sort by group, and (only) if two values share the same group, sort by the original value
return a.group - b.group || a.value - b.value
});
//apply data to current Array
intermediate.forEach(function(obj, i){ arr[i] = obj.value });
return arr;
//return new Array
//return intermediate.map(function(obj){ return obj.value });
}
case 2: treating an Array like it's first value
[2,1,3,4,1,[4,6,2,4],2,4,1] -> [1,1,1,2,2,[2,4,4,6],3,4,4]
function sort2(arr){
//an utility to fetch the first non-array value recursively
function _value(v){
while(Array.isArray(v)) v = v[0];
return v;
}
var intermediate = arr.map(function(v, i){
if(Array.isArray(v)) v = sort2(v);
return {
index: i,
value: v,
sortingValue: _value(v)
}
}).sort(function(a, b){
return a.sortingValue - b.sortingValue || a.index - b.index;
});
//apply data to current Array
intermediate.forEach(function(obj, i){ arr[i] = obj.value });
return arr;
//return new Array
//return intermediate.map(function(obj){ return obj.value });
}
This is the perfect solution, use nested function invoke to sort array.
Firstly , store all the array position and sub array.
Secondly, extract numbers into new array,
Finally insert sorted array into same position as before.
var a = [2,1,3,4,1,[4,6,[4,5,[7,3,2,1,6],1,2],2,4],2,4,1];
function nestedSort(arr){
var items = [];
var numArr = [];
for ( key in arr){
if (arr[key] instanceof Array)
{
items.push({index:key,array:arr[key]});
}else{
numArr.push(arr[key]);
}
}
numArr.sort();
for (key in items){
numArr.splice(items[key].index,0,nestedSort(items[key].array));
}
return numArr;
}
console.log(nestedSort(a));
[
1,
1,
1,
2,
2,
[
2,
4,
[
1,
2,
[
1,
2,
3,
6,
7
],
4,
5
],
4,
6
],
3,
4,
4
]
Hope this can solve your problem. :)
You can loop over array and remove all sub arrays and save their index and then sort the new array and again push sorted sub arrays on specific indexes.
Sample
var arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 4, 1];
var arr1 = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 6, 4, [4, 5, 3], 1, 2, 1, 3]
var a = [2,1,3,4,1,[4,6,[4,5,[7,3,2,1,6],1,2],2,4],2,4,1];
function mySort(arr) {
var _list = [];
arr.forEach(function(item, index) {
if (Array.isArray(item)) {
_list.push({
index: index,
value: arr.splice(index, 1).pop()
});
}
});
arr.sort();
_list.forEach(function(item) {
arr.splice(item.index, 0, mySort(item.value))
})
return arr;
}
console.log(mySort(arr.slice()))
console.log(mySort(arr1.slice()))
console.log(mySort(a.slice()))
Edit 1
Inspired from joey-etamity's answer, have made it generic for nested structure.
No, you don't put the sort call in the comparison function. You would recurse through your arrays, bottom to top, and sort them one after the other. In your case you might not even need recursion if it's only one array in another:
a.forEach(function(element) {
if (Array.isArray(element))
element.sort(function compare(a, b) { return a-b; });
})
(I've chosen a simple numerical compare here).
Then you'd sort the outer array:
a.sort(function compare(a, b) {
if (Array.isArray(a)) a = a[0];
if (Array.isArray(b)) b = b[0];
return a - b;
})
(here compare takes the first element of the array to compare by that against the other numbers).
I suggest to splice the array if there is an element an array. Then sort the array and reassemble the array.
This proposal iterates from the back and keeps the array intact while splicing.
function sort(array) {
var i = array.length,
inside = [];
while (i--) {
if (Array.isArray(array[i])) {
inside.unshift({ pos: i, value: sort(array.splice(i, 1)[0]) });
}
}
array.sort(function (a, b) { return a - b; });
inside.forEach(function (a) {
array.splice(a.pos, 0, a.value);
});
return array;
}
var a = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 4, 1];
console.log(sort(a));
I think this would be better to use Array.prototype.sort this way:
// var arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 4, 1];
var arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 6, 4, [4, 5, 3], 1, 2, 1, 3];
var chunks = chunkate(arr)
console.log(JSON.stringify(chunks));
chunks.forEach(ch => ch.sort(_sort));
var result = chunks.reduce((p, c) => p.concat(c));
console.log(JSON.stringify(result));
function _sort(a, b) {
var isAa = Array.isArray(a),
isAb = Array.isArray(b);
isAb && b.sort(_sort);
return (isAa || isAb) ? 0 : a - b;
}
function chunkate(arr) {
return arr.reduce((a, c) => {
Array.isArray(c) ? a.push(chunkate(c), []) : a[a.length - 1].push(c)
return a;
}, [[]]);
}
How it works?
If items to compare are are array then they shouldn't be replaced so by sending false sort function recognize that there is no need to replace. Otherwise the simple compare is the answer.
Edit
As discussed in comments, it's better to separate values to chunks and then sort each part then join parts again. If nesting depth is only one level you can use default sort (without _sort function) but be aware of array in array used for nested array. So the sort should be changed like this:
chunks.forEach(ch => Array.isArray(ch[0])? ch[0].sort(): ch.sort());

ReverseArrayInPlace Strange error?

I was reading the eloquent book, did a little assignment and ran into something that i can't really figure out. why return gives one answer and console.log gives a different one?
function reverseArrayInPlace(r){
var z = [];
for(var c = (r.length)-1;c>=0;c--){
z.push(r[c]);
}
console.log(z);
return z;
}
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
Result:
[5, 4, 3, 2, 1] <- console.log(z)
[1, 2, 3, 4, 5] <- return z
You aren't reversing the array in place. You are creating a new array that is returned by reverseArrayInPlace
EDIT: SOLUTION
Instead of creating a new array and returning it, simply replace the contents of the original array with the one you reversed by using this line at the end of your function:
r.splice.apply(r,[0,r.length].concat(z))
This line removes all elements from array r and replaces them with all elements from array z and then you can simply return r
function reverseArrayInPlace(r){
var z = [];
for(var c = (r.length)-1;c>=0;c--){
z.push(r[c]);
}
console.log(z);
r.splice.apply(r,[0,r.length].concat(z));
return r;
}
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
To reverse the array "in place," work on the original array rather than creating a new one:
function reverseArrayInPlace(r) {
var t, i, j;
for(i = 0, j = r.length - 1 ; i < j ; i++, j--) {
t= r[i];
r[i]= r[j];
r[j]= t;
}
}
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
z is not the same array as arrayValue. Inside your function you create a new array, called z, where you will push arrayValue's elemenets in reversed order. Instead of that, might I suggest the following:
function reverseArrayInPlace(r){
var aux;
for(var c = 0; c < r.length / 2; c++){
aux = r[c];
r[c] = r[r.length - 1 - c];
r[r.length - 1 - c] = aux;
}
}
arrays are passed by reference by default, so if you call reverseArrayInPlace(arrayValue), then after it is executed, arrayValue will be in reversed order. Note, that you are traversing only half of the array, since if you traverse the whole array, then you will switch the elements back.

Categories