Related
I need to create a function that take as parameter an array and a target. It should return an array of arrays where the sum of these numbers equals to the target
sumPairs(array, target) {
}
For example:
sumPairs([1, 2, 3, 4, 5], 7) // output : [[2, 5], [3, 4]]
I know I have to use map(), and probably reduce(), set(), or filter() maybe (I read their documentation in MDN but still cant find out). I tried some ways but I can't get it.
If you guys could help me to find out how to dynamically create arrays and push them into a new array..
I read there some solutions (Split array into arrays of matching values) but I hate to just use created functions without knowing what they really do or how they work.
Some very basic code for achieving it, Just run all over combinations and conditionally add the items you want.
function sumPairs(array, target) {
var res = [];
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array.length; j++){
if(i!=j && array[i]+array[j]==target &&
res.filter((x)=> x[0] == array[j] && x[1] == array[i]).length == 0 )
res.push([array[i], array[j]]);
}
}
return res;
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
Option 2 - see this answer for more options (like using reduce)
function sumPairs(array, target) {
return array.flatMap(
(v, i) => array.slice(i+1).filter(w => (v!=w && v+w==target)).map(w=> [w,v])
);
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
"The exercise says that it sould be arrays of pairs that sum the
target value so I think only 2 items"
If you need a pair that matches a sum and you pick any number from the list, you are left with
the following equation to solve num + x = sum where we want to find x. E.g. if you picked 7 and the target sum is 10 then you know you are looking for a 3.
Therefore, we can first construct a counting map of the numbers available in our list linear (O(n)) time and then search for matches in linear time as well rather than brute forcing with a quadratic algorithm.
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
return nums.reduce((pairs, num) => {
countByNum[num]--;
const target = sum - num;
if (countByNum[target] > 0) {
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[num]++;
}
return pairs;
}, []);
}
function countGroupByNum(nums) {
return nums.reduce((acc, n) => (acc[n] = (acc[n] || 0) + 1, acc), {});
}
Here's another implementation with more standard paradigms (e.g. no reduce):
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
const pairs = [];
for (const num of nums) {
const target = sum - num; //Calculate the target to make the sum
countByNum[num]--; //Make sure we dont pick the same num instance
if (countByNum[target] > 0) { //If we found the target
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[target]++; //Didin't find a match, return the deducted num
}
}
return pairs;
}
function countGroupByNum(nums) {
const countByNum = {};
for (const num of nums) {
countByNum[num] = (countByNum[num] || 0) + 1;
}
return countByNum;
}
You can also sort your array and find all the pairs with given sum by using two pointer method. Place the first pointer to the start of the array and the second pointer to the end.
if the sum of the values at the two places is :
More than target: Decrement your second pointer by 1
Less than target: Increment your first pointer by 1
Equal to target: This is one possible answer, push them to your answer array and increment your first pointer by 1 and decrement your second pointer by 1.
This is more performant solution with complexity O(n*log(n))
I have an array that I created using Array(...) and Array.prototype.map, like this:
var array_name = Array(255).map(function(undef, i) {
return i + 1;
});
The values of this array are:
[1, 2, 3, ..., 253, 254, 255]
This is an array that won't get modified, so the first value of this array will always be 1 and the last value of this array will always be 255.
I already know the index of each value is {value} - 1, so 200 would be 199, 199 would be 198, so on and so forth.
Let's say I want 255's opposite value, which would be 0, I could get that using array_name[0], but what if I wanted 200's opposite value, how would I know what the opposite index of 199 is so I could get it's value?
Do:
opposite_index = arr.length - index - 1
For example:
a = [1,2,3,4,5,6,7,8,9,10]
index = 3
a[index]
4
It's opposite is 7 so:
opposite_index = a.length - index - 1
a[opposite_index]
7
With reverse as per #Maheer Ali suggestion:
a.reverse()[index]
7
Your Array(255).map() create undefined array value.So do with Array#from length object.And pass your value.get index of the value and match with reverse array you get opposite value
let check = (val) => {
var array_name = Array.from({length:255},(a,b)=>b+1);
var nor_ind = array_name.indexOf(val);
var re_in = array_name.reverse(array_name).indexOf(val)
return ({nor_val:val,nor_ind:nor_ind,opp_val:re_in})
}
console.log(check(254))
First of all the code you provided doesn't create array [1,2,3...255]. It will create it will 255 empty items first you need to fill().
var arr = Array(255).fill().map((a,i) => i+1);
//Create an array which will have revese items.
let revarr= arr.reverse()
console.log(revarr[0]) //255
console.log(revarr[254]) // 1
If you don't want to create a reverse arr. You can create a function
var arr = Array(255).fill().map((a,i) => i+1);
const opp = (arr,num) => arr[arr.length - num - 1];
console.log(opp(arr,0));
console.log(opp(arr,254));
First, you gotta understand that there is weird behavior concerning Array(n).map(f) (it won't create the array you're expecting), see this answer for explanation, second, do this to get the opposite values:
/* fill it first with .fill(), see the question I linked for more explanation */
var array = Array(255).fill(undefined).map(function(undef, i) {
return i + 1;
});
function opposite(array, n) {
return array[array.length - n];
}
console.log(opposite(array, 255));
console.log(opposite(array, 200));
console.log(opposite(array, 199));
console.log(opposite(array, 1));
Notice that length - n is used instead of length - n - 1, because we're dealing with values from 1 to n, not from 0 to n - 1.
Subtract the index from (length-1) -> max index of the array
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function findOpp(index, length) {
maxIndex = length - 1;
if (index <= maxIndex && index >= 0) {
return maxIndex - index;
} else {
return 'You have enter a wrong index';
}
}
console.log(findOpp(-1, 10));
console.log(findOpp(0, 10));
console.log(findOpp(1, 10));
console.log(findOpp(2, 10));
console.log(findOpp(4, 10));
console.log(findOpp(5, 10));
console.log(findOpp(6, 10));
console.log(findOpp(7, 10));
console.log(findOpp(8, 10));
console.log(findOpp(9, 10));
console.log(findOpp(10, 10));
Using Maheer Ali's suggestion I managed to get the desired result by reversing the array and using indexOf to get the index of that number:
var numbers = Array(255).map(function(v, i) {
return i + 1;
});
var opposite_brightness = numbers.reverse()[numbers.indexOf(brightness)];
I am dynamically slicing an array, and I can get the functionality I want by simply removing the last element with arr.pop() but I want to know exactly why my while loop is adding this to my array when it breaks my conditional.
slices(num){
let arr = [this.digits.slice(0, num)]
let i = 0
if (this.digits.length < num){
throw new Error('Slice size is too big.')
} else {
while (arr[i].length === num){
i++
arr.push(this.digits.slice(i, num + i))
}
// arr.pop() - removed for testing
return arr
}
}
Here is an example. Let's say we want to slice this array:
this.digits = [ 3, 1, 0, 0, 1 ]
Ideally, our output will look like this:
[3, 1, 0], [1, 0, 0], [0, 0, 1]]
With my current code and without using arr.pop(), my algorithim will consistently sneak in an extra slice iteration that has less length than what my conditional is asking for (in this case, num == 3)
This will be my output:
[[3, 1, 0], [1, 0, 0], [0, 0, 1], [0, 1]]
I know there are many ways to do this, but for this, I would like to maintain the integrity of my code, so a solution that uses my implementation would be great :D
EDIT: I get why the last element is being added. Since the element before fulfills the conditional (it's length is equal to num), it moves on to the next iteration but how I do handle it eloquently without using .pop()
EDIT: Thanks all for the answers! They all seem like they would work, but Peter B's implementation was so very clean, especially given that he changed just a few lines for me and it worked like a charm. Thanks again!
You are checking the wrong condition in the while. It is better to calculate how many sub-arrays you are going to add inside the while (in addition to the one that you start with), and count up to that number, like this:
var digits = [3, 1, 0, 0, 1];
function slices(num) {
let arr = [this.digits.slice(0, num)]
let i = 0
if (this.digits.length < num) {
throw new Error('Slice size is too big.')
} else {
var sliceCutoff = this.digits.length - num;
while (i < sliceCutoff) {
i++
arr.push(this.digits.slice(i, num + i))
}
return arr
}
}
console.log(slices(3));
You need to check if the leftover items are enough to get an array with the wanted length. That means, you need a single loop with a continuing check for the actual ster index, wanted size and the length.
An approach by checking the last array after splicing is unnecessary, because it generates an avoidable overhead.
function slice(array, n) {
var result = [],
start = 0;
while (start + n <= array.length) {
result.push(array.slice(start, start++ + n));
}
return result;
}
var array = [3, 1, 0, 0, 1];
console.log(slice(array, 3));
Description
I believe that you're looking for something along the lines of this? As you can see, I've also removed some redundant code, i.e. using a while loop in this scenario and the else clause.
I've also just declared digits as a parameter for this demo, I believe that you'd have the initiative to be able to change this to your application(s) requirement(s) without much/any assistance.
function slices(digits, num) {
const arr = [];
if (digits.length < num)
throw new Error('Slice size is too big.')
for (let i = 0; i != num; i++)
arr.push(digits.slice(i, num + i));
return arr;
}
var d = [3, 1, 0, 0, 1]; // Only here for the demo.
console.log(slices(d, 3));
You're really close. I think my proposed solution here keeps the general idea of yours. The problem you're hitting is that checking arr[i].length being equal to num means this is only checking the last item you added to the array, not the next one. Instead, check the item you're about to add.
this.digits = [ 3, 1, 0, 0, 1 ];
function slices(num) {
let arr = []
let i = 0
if (this.digits.length < num) {
throw new Error('Slice size is too big.')
} else {
while (this.digits.slice(i, num + i).length === num){
arr.push(this.digits.slice(i, num + i))
i++
}
// arr.pop() - removed for testing
return arr
}
}
console.log(slices(3));
Javascript is something new for me, and we have to do homework.
I have created new array:
var numbers = [1,2,3,4,5,6];
And with function forEeach I should achieve result like in console.log:
console.log(numbers[0]*numbers[1]+numbers[0]+numbers[1]);
I've tested many things, but I don't have any idea how to pull out signle init...
I know it should be simple, but I've stucked.
Thanks for help!
From your question looks like your problem is interacting with the current element of the forEach loop.
var numbers = [1,2,3,4,5,6]
// this will print every number in the array
// note that index numbers are not needed to get elements from the array
numbers.forEach(function(num){
console.log(num)
})
Now, if what you're trying t achieve is sum and multiply every int (as stated in the question title), you could do it like this
var numbers = [1,2,3,4,5,6]
var sumResult = 0
var multiplicationResult = 1
// the function will be evaluated for every element of the array
numbers.forEach(function(num){
sumResult += num
multiplicationResult *= num
})
console.log('Sum', sumResult)
console.log('Multiplication', multiplicationResult)
However, a more appropiate approach could be obtained by using reduce like this:
var numbers = [1,2,3,4,5,6]
var sumResult = numbers.reduce(function(result, num){
return num+result
}, 0)
var multiplicationResult = numbers.reduce(function(result, num){
return num*result
}, 1)
console.log('Sum', sumResult)
console.log('Multiplication', multiplicationResult)
Hope this helps.
More info:
Reduce # MDN
ForEach # MDN
To pull out a single number for the provided array you using the indexer/bracket notation which is specifying a number (length of array - 1) in brackets, like below:
var numbers = [1, 2, 3, 4, 5, 6];
numbers[0]; // selects the first number in the array
numbers[1]; // selects second number etc.
To sum up the numbers using forEach, simply do:
var sum = 0;
numbers.forEach(function(number) {
sum += number; // add number to sum
});
forEach goes through all the numbers in the numbers array, passing in each number to the function defined and then adds the number to the sum variable.
If you want your results, use map(). Unlike forEach(), map() will always return results in a new array. It wasn't very clear as to what expression you are expected to use or what the result of said expression should be so this demo will do the following on each iteration:
A = current value * next value
B = current value + next value
C = A + B;
Demo
const num = [1, 2, 3, 4, 5, 6];
let arr = num.map(function(n, idx, num) {
let next = num[idx + 1];
if (!next > 0) {
next = idx + 2;
}
let subSUM = n + next;
let subPRD = n * next;
let subRES = subPRD + subSUM;
return subRES;
});
console.log(arr);
Given an array with a minimum length of 3 and a maximum length of 5, which always contains uniquely occurring integers from 0 to 4 in ascending order, I need to pick out two non-consecutive numbers from it. Non-consecutive refers to their numeric value, not their position in the array.
To clarify, here are examples of valid arrays:
[ 1, 2, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 3, 4 ]
For the arrays above, valid answers could be, respectively:
[ 1, 3 ]
[ 0, 2 ], [ 0, 4 ] or [ 1, 4 ]
[ 0, 3 ] or [ 0, 4 ]
Furthermore, in those cases where there is more than one valid answer, I need it to be selected at random, if at all possible (for instance I don't want to favor sequences that begin with the lowest number, which is what would occur if I always began checking from left to right and stopped checking as soon as I found one valid solution).
What would be the most efficient way of tackling this problem in Javascript?
You could use two nested iterations and build an new array for choosing as random result.
function getNonConsecutives(array) {
return array.reduce((r, a, i, aa) => r.concat(aa.slice(i + 2).map(b => [a, b])), []);
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to Bee157's answer, you could use a random choice with a constraint, like length for the first index and add the needed space for the second index.
The problem is, due to the nature of choosing the first number first, the distribution of the result is not equal.
function getNonConsecutives(array) {
var i = Math.floor(Math.random() * (array.length - 2));
return [
array[i],
array[Math.floor(Math.random() * (array.length - 2 - i)) + 2 + i]
];
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
demoFn(array) {
var i,j, y =[];
for (i=0; i<=array.length;i++) {
for (j = i + 1; j <= array.length; j++) {
if (array[j] && array[i]) {
if (array[j] !== array[i] + 1) {
y.push([array[i], array[j]]);
}
}
}
}
}
Take a random array and check it.
You can create a function using recursion that will pick random number in each iteration and loop all other elements and if condition is met add to array.
function findN(data) {
data = data.slice();
var r = []
function repeat(data) {
if (data.length < 2) return r;
var n = parseInt(Math.random() * data.length);
data.forEach(function(e, i) {
if (i != n) {
var a = data[n];
if (Math.abs(a - e) != 1 && r.length < 2) r.push(n < i ? [a, e] : [e, a])
}
})
data.splice(n, 1);
repeat(data)
return r;
}
return repeat(data)
}
console.log(findN([1, 2, 3]))
console.log(findN([0, 1, 2, 4]))
console.log(findN([0, 3, 4]))
Something like this should do it:
const pick = nums => {
// Pick a random number
const val = nums[Math.floor(Math.random() * nums.length) + 0];
// Filter out any numbers that are numerically consecutive
const pool = nums.filter(n => Math.abs(n - val) > 1);
// Pick another random number from the remainer
const other = pool[Math.floor(Math.random() * pool.length) + 0];
// Sort + return them
return [val, other].sort();
};
console.log(pick([0, 1, 2, 4]));
since you state that the array ellemnts are all unique, and that they are sorted.
It should suffice to take an random element
var index1=Math.floor(Math.random()*arr.length)
now any other element (except maybe the elemnts on position (index1 +/- 1) are not consecutive
So a new random element can be chosen excluding the first index.
var index2=Math.floor(Math.random()*arr.length);
if(index2==index1){
index2+=((index2<arr.length-1)?1:-1);
}
if(Math.abs(arr[index1]-arr[index2])<=1){
if(index2==0 && arr.length<4){
//set index2 to arr.length-1 and do check again, if not ok=> no result
if(!(arr[index1]-arr[arr.length-1]>=-1)){
return [arr[arr.length-1],arr[index1]];
}
}
else if(index2==arr.length-1 && arr.length<4){
//set index2 to 0 and do check again, if not ok=> no result
if(!(arr[index1]-arr[0]<=1)){
return [arr[0],arr[index1]];
}
}
else{
//if index2>index1 index2++
//else index2--
//always OK so no further check needed
index2+=(index2>index1?1:-1);
return [arr[index1],arr[index2]];
}
}
else{
//ok
return [arr[index1,arr[index2]];
}
return false;
if speed is not important, you can use a filter on the array to calculate a new array with all elements differing more then 1 unit of arr[index1]. and randomly select a new number from this new array.
Other attempt
function getNonConsecutive(arr){
var index1,index2,arr2;
index1=Math.floor(Math.random()*arr.length);
arr2=[].concat(arr);
arr2.splice((index1!==0?index1-1:index1),(index!==0?3:2));
if(arr2.length){
index2=Math.floor(Math.random()*arr2.length);
return [arr[index1],arr2[index2]];
}
else{
//original array has length 3 or less
arr2=[].concat(arr);
arr2.splice(index1),1);
for (var j=0,len=arr.length;j<len;j++){
if(Math.abs(arr1[index1]-arr2[j])>1){
return [arr[index1],arr2[j]];
}
}
}
return false
}