Recursively square each number in an array [duplicate] - javascript

This question already has answers here:
Javascript recursive array flattening
(27 answers)
Closed 1 year ago.
I came up with the following solution for returning an array of squared numbers, sorted in ascending order.
function sortedSquaredArray(array) {
let squaredArray = [];
for(i=0;i<array.length;i++){
squaredArray.push(array[i] ** 2);
}
return squaredArray.sort((a,b) => a-b);
}
I'm trying to get better at writing recursions, so I'm attempting to turn the above into a pure recursive function (with no inner function).
This is my attempt.
function sortedSquaredArray(array) {
let squaredArray = [];
squaredArray.push(array[0] ** 2);
if(array.length) sortedSquaredArray(array.shift());
return squaredArray.sort((a,b) => a-b);
}
I believe its the sortedSquaredArray(array.shift()) that's causing this to fail.. I thought about using .slice and .concat somehow, but struggling to wrap my head around it.
What am I missing?

.shift returns the removed element - it doesn't return the array after being mutated.
For proper recursion, you also either need to pass along the array as a parameter, or create it during the final iteration and return it back for the prior callers to insert their items into.
So that you only sort at the end, I'd recommend passing it as a parameter, so that the final call with no recursion can sort.
function sortedSquaredArray(input, squares = []) {
if (!input.length) {
return squares.sort((a, b) => a - b);
}
squares.push(input.pop() ** 2);
return sortedSquaredArray(input, squares);
}
function sortedSquaredArray(input, squares = []) {
if (!input.length) {
return squares.sort((a, b) => a - b);
}
squares.push(input.pop() ** 2);
return sortedSquaredArray(input, squares);
}
console.log(sortedSquaredArray([2, 4, 1]));
A similar option that doesn't mutate the parameter:
function sortedSquaredArray(input, squares = []) {
if (!input.length) {
return squares.sort((a, b) => a - b);
}
const [firstItem, ...rest] = input;
squares.push(firstItem ** 2);
return sortedSquaredArray(rest, squares);
}
or
function sortedSquaredArray(input, squares = []) {
if (!input.length) {
return squares.sort((a, b) => a - b);
}
squares.push(input[0] ** 2);
return sortedSquaredArray(input.slice(1), squares);
}
If you want just one parameter, then do the other approach I mentioned - create the array during the final iteration and return it back up:
const recurse = (input) => {
if (!input.length) {
return [];
}
const square = input.pop() ** 2;
return sortedSquaredArray(input).concat(square);
};
function sortedSquaredArray(input) {
return recurse(input).sort((a, b) => a - b);
}
console.log(sortedSquaredArray([2, 4, 1]));

I would separate the sorting from the squaring, since the squaring might be useful on its own. This is a useful way to do this sort of recursion for smallish problems:
const squaredArray = ([n, ...ns]) =>
n == undefined
? []
: [n * n, ... squaredArray (ns)]
const sortedSquaredArray = (ns) =>
squaredArray (ns) .sort ((a, b) => a - b)
console .log (sortedSquaredArray ([4, 9, 2, 5, 8]))

Related

Calculating second largest number in array (Javascript) : Seems to work in Sandbox but fails Hackerrank testing

Tried to determine the second largest number in an array (Javascript) on CodeSandbox. It seems to work fine, but it fails the CodeWars testing. I have added a dummy array just to run my own tests in Sandbox.(Have mercy, I'm a beginner and this is my first StackOverFlow question)
const nums = [3, 100.3, 88, 1, -2.4, 9, 18];
const getSecondLargest = (nums) => {
const descending = nums.sort((a, b) => b - a);
return descending[1];
};
console.log(getSecondLargest(nums)); // console returns 88
EDIT: Okay so I with my super-tired brain I said CodeWars, when I actually meant Hackerrank (so sorry!). I realized they didn't necessarily test with NaNs, but they did have repeating numbers, so using the index of [1] isn't ideal. The exercise is from the 10 Days of Javascript - Day 3: Arrays https://hackerrank.com/domains/tutorials/10-days-of-javascript
So I now tried this code below, and it passes...but my code seems a bit janky, is there a cleaner way to write this, and can I combine it with the isNan logic then?
const nums = [3, 100, 88, 100, -2.4, 9, 18];
const getSecondLargest = (nums) => {
const ascending = nums.sort((a, b) => a - b);
if (ascending[ascending.length - 2] === ascending[ascending.length - 1]) {
return ascending[ascending.length - 3];
} else {
return ascending[ascending.length - 2];
}
};
console.log(getSecondLargest(nums)); // console returns 88
It looks like there maybe strings in the array and you need to handle that. Here are a few ways:
One is to filter the non-numerical stuff out before sorting. You can use isNaN() to test if an object "is not a number".
const getSecondLargest = (nums) => {
const descending = nums
.filter(n => !isNaN(n))
.sort((a, b) => b - a);
return descending.length < 2 ? undefined : descending[1];
};
Another option is to handle the strings in sorting. Push them to the end of the array:
const getSecondLargest = (nums) => {
const descending = nums.sort((a, b) => {
if (isNaN(a) && isNaN(b)) return 0;
if (isNaN(a)) return 1;
if (isNaN(b)) return -1;
return b - a;
});
return descending.length < 2 || isNaN(descending[1]) ? undefined : descending[1];
};
A third way is a simple for loop that keeps track of the 2 highest values:
const getSecondLargest = (nums) => {
let max1 = undefined;
let max2 = undefined;
for (let n of nums) {
if (isNaN(n)) continue;
if (max2 === undefined || n > max2) {
if (max1 === undefined || n > max1 ) {
max2 = max1;
max1 = n;
}
else {
max2 = n;
}
}
}
return max2;
}

Chain methods and call function one by one

so basically I need to implement a lazy evaluation. .add() takes a function as parameter and any other arbitrary arguments. The function that is passed as an argument is run later (when evaluate is called) with other arguments(if any) as parameters to that function.
Basically my issue stands when i run .evaluate() which takes an array as parameter, the functions that were passed to add() as parameters are not called one at a time and returning a result and have it as a parameter for the next.
class Lazy {
constructor() {
this.functions = [];
this.counter = 0;
this.resultedArray = [];
}
add(func, ...args) {
if (args.length > 0) {
this.counter++;
const argsArrayNumeric = args.filter((item) => typeof item === "number");
this.functions.push({
func: func,
args: args,
});
} else {
if (func.length <= args.length + 1 || func.length === args.length) {
this.counter++;
this.functions.push({ func: func });
} else {
console.log("Missing parameters for " + func.name + " function");
}
}
return this;
}
evaluate(array) {
if (this.counter > 0) {
let result;
this.functions.map((obj, index) => {
array.map((item, index) => {
console.log(obj);
if (obj.func && obj.args) {
result = obj.func(item, ...obj.args);
} else {
result = obj.func(item);
}
this.resultedArray.push(result);
});
});
console.log(this.resultedArray);
} else {
console.log(array);
}
}
}
const s = new Lazy();
const timesTwo = (a) => {
return a * 2;
};
const plus = (a, b) => {
return a + b;
};
s.add(timesTwo).add(plus, 1).evaluate([1, 2, 3]);
//correct result is [3,5,7] but i have [ 2, 4, 6, 2, 3, 4 ]
There are a few problems in evaluate:
push will be executed too many times when you have more than one function.
Don't use map when you don't really map. .map() returns a result.
From the expected output (in the original version of the question) it seems you need to apply the functions from right to left, not from left to right.
this.counter does not really play a role. The length of the this.functions array should be all you need.
the result should not be printed in the method, but returned. It is the caller's responsibility to print it or do something else with it.
All this can be dealt with using reduce or reduceRight (depending on the expected order) like this:
evaluate(array) {
return this.functions.reduceRight((result, obj) =>
result.map((item) => obj.func(item, ...(obj.args || [])))
, array);
}
And in the main program, print the return value:
console.log(s.add(plus, 1).add(timesTwo).evaluate([1, 2, 3]));
The add method has also some strange logic, like:
When the first if condition is false, then the else block kicks in with args.length == 0. It is then strange to see conditions on args.length... it really is 0!
If the first condition in func.length <= args.length + 1 || func.length === args.length is false, then surely the second will always be false also. It should not need to be there.
argsArrayNumeric is never used.
All in all, it seems the code could be reduced to this snippet:
class Lazy {
constructor() {
this.functions = [];
}
add(func, ...args) {
if (func.length > args.length + 1) {
throw ValueError(`Missing parameters for ${func.name} function`);
}
this.functions.push({ func, args });
return this;
}
evaluate(array) {
return this.functions.reduceRight((result, obj) =>
result.map((item) => obj.func(item, ...(obj.args || [])))
, array);
}
}
const timesTwo = (a) => a * 2;
const plus = (a, b) => a + b;
const s = new Lazy();
console.log(s.add(plus, 1).add(timesTwo).evaluate([1, 2, 3])); // [3, 5, 7]
I think you're working too hard at this. I believe this does almost the same thing (except it doesn't report arity errors; that's easy enough to include but doesn't add anything to the discussion):
class Lazy {
constructor () {
this .fns = []
}
add (fn, ...args) {
this .fns .push ({fn, args});
return this
}
evaluate (xs) {
return xs .map (
x => this .fns .reduce ((a, {fn, args}) => fn (...args, a), x)
)
}
}
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const s = new Lazy () .add (timesTwo) .add (plus, 1)
console .log (s .evaluate ([1, 2, 3]))
But I don't actually see much of a reason for the class here. A plain function will do much the same:
const lazy = (fns = [], laze = {
add: (fn, ...args) => lazy (fns .concat ({fn, args})),
evaluate: (xs) => xs .map (
x => fns .reduce ((a, {fn, args}) => fn (...args, a), x)
)
}) => laze
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const s = lazy () .add (timesTwo) .add (plus, 1)
console .log (s .evaluate ([1, 2, 3]))
This is slightly different in that s is not mutated on each add, but a new object is returned. While we could change that and mutate and return the original object easily enough, the functional programming purist in me would actually consider moving more in that direction. In fact, all we're maintaining here is a list of {fn, args} objects. It might make the most sense to take that on directly, like this:
const add = (lazy, fn, ...args) => lazy .concat ({fn, args})
const evaluate = (lazy, xs) => xs .map (
x => lazy .reduce ((a, {fn, args}) => fn (...args, a), x)
)
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const q = []
const r = add (q, timesTwo)
const s = add (r, plus, 1)
// or just
// const s = add (add ([], timesTwo), plus, 1)
console .log (evaluate (s, [1, 2, 3]))
In this version, we don't need a Lazy constructor or a lazy factory function, as our data structure is a plain array. While this is the format I prefer, any of these should do something similar to what you're trying.
Update
Based on comments, I include one more step on my journey, between the first and second snippets above:
const lazy = () => {
const fns = []
const laze = {
add: (fn, ...args) => fns .push ({fn, args}) && laze,
evaluate: (xs) => xs .map (
x => fns .reduce((a, {fn, args}) => fn (...args, a), x)
)
}
return laze
}
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const s = lazy () .add (timesTwo) .add (plus, 1)
console .log (s .evaluate ([1, 2, 3]))
This version may be more familiar than the second snippet. It stores fns in a closure, hiding implementation details in a way we often don't with JS classes. But that list is still mutable through the add method of the object we return. Further ones proceed down the road to immutability.

multiplyOddByTwo function using javascript

I'm trying to get the odd numbers in the following array and then multiply by 2 using reduce method but it's giving me undefined error. Any help please.
const multiplyOddByTwo = (arr) => {
return arr.reduce((acc, curr) => {
if (curr % 2 === 0) {
arr.push(curr);
} else {
arr.push(curr * 2)
}
}, [])
}
console.log(multiplyOddByTwo([1, 2, 3]));
Two things to point out:
You have to make modifications to the second param in your reduce function. It is your initial value. The first param in the reduce callback(acc) is your cumulated value up till that particular iteration.
You have to return your cumulated value in each iteration. (When in the final iteration, this will be your calculated answer. Here you aren't returning anything hence the undefined)
const multiplyOddByTwo = (arr) => {
return arr.reduce((acc, curr) => {
if (curr % 2 === 0) {
acc.push(curr);
} else {
acc.push(curr * 2)
}
return acc;
}, [])
}
console.log(multiplyOddByTwo([1, 2, 3])); // [2,2,6]
This multiplies odd index elements by 2.
Edit: That is not undefined error. That is just undefined being returned. Any function not returning anything returns undefined.
If you would like it to only return odd numbers multiplied by 2 and not return the even then use this:
const multiplyOddByTwo = (arr) => {
const odd = arr.filter(num => num % 2 !== 0);
return odd.map(i => i * 2);
}
console.log(multiplyOddByTwo([1, 2, 3]));
Otherwise if you would like it to return all numbers but multiply odd numbers by 2 then use this:
const multiplyOddByTwo = (arr) => {
return arr.map(num => num % 2 === 0 ? num : num * 2);
}
console.log(multiplyOddByTwo([1, 2, 3]));

Time and space complexity of the two different approaches

I am trying to learn how to estimate time and space complexity of different iterations and need some help with my second approach. What's the difference in terms of space & time complexity on those two functions below?
This should be O(n) time and O(n) space.
function twoNumberSum(array, targetSum) {
let nums = {};
for (const num of array) {
const potentialMatch = targetSum - num;
if (potentialMatch in nums) {
return [potentialMatch, num].sort((a, b) => a - b)
} else {
nums[num] = true
}
}
return []
}
console.log(twoNumberSum([3, 5, -4, 8, 11, 1, -1, 6], 10))
However, this approach?
function twoNumberSum(array, targetSum) {
const map = new Map(array.map(item => [item, item]));
for (let [key, value] of map) {
const missingInc = targetSum - value;
if (missingInc !== value && map.has(missingInc)) {
return [value, map.get(missingInc)].sort((a, b) => a - b)
}
}
return [];
}
console.log(twoNumberSum([3, 5, -4, 8, 11, 1, -1, 6], 10))
Also, final question... when I sort the return statement with .sort((a, b) => a - b) should this be defined as O(log(n))?
I would suggest to first refactor them to use the same data structure, so that differences become more visible:
function twoNumberSum(array, targetSum) {
let nums = new Set();
for (const num of array) {
const potentialMatch = targetSum - num;
if (nums.has(potentialMatch) {
return [potentialMatch, num].sort((a, b) => a - b)
} else {
nums.add(num)
}
}
return []
}
function twoNumberSum(array, targetSum) {
const nums = new Set();
for (const num of array)
nums.add(num);
// alternatively, use const nums = new Set(array), but the loop is clearer
for (let num of array) {
const missingInc = targetSum - num;
if (missingInc !== value && nums.has(missingInc)) {
return [value, missingInc].sort((a, b) => a - b)
}
}
return [];
}
Before considering performance, we need to evaluate the correctness/equivalence: notice that the second function doesn't work when the input value contains duplicates. But let's assume that this is given as a precondition (can never happen), in which case the results are always the same.
The difference is that one approach always constructs the whole lookup map before testing values, while the other builds it on the go. While both have O(n) worst case complexity, the best case complexity for the first approach is O(1) while it is O(n) for the second. What the average complexity is depends on how your inputs are distributed.
When I sort the return statement with .sort((a, b) => a - b) should this be defined as O(log(n))?
No. The complexity of a sort is generally O(n log n), but where n refers to the length of the array to be sorted. In your case, you're always sorting an array of two items, which has constant complexity. It does not depend on the size of your input n (assumed to be the array.length, although in some numerical problems you also need to consider the size of the numbers).

Getting range numbers using recursion in JavaScript

I am trying to get the range of numbers using recursion. Can someone explain to me why it isn't working?
function range(x,y){
var results = [];
if(x === y){
return results;
}
return results.push(range(x + 1,y));
}
range(1,5);
The beauty of recursion is that you don't need local variables (var results). You just pass state as arguments to each recursive iteration:
const concat = (xs, y) => xs.concat(y);
const range = (x, y) => {
const rec = (x, y, acc) => x < y ? rec(x + 1, y, concat(acc, x)) : acc;
return rec(x, y, []);
}
ES5 version in case you aren't familiar with the arrow syntax:
function concat(xs, y) {
return xs.concat(y);
}
function range(x, y) {
function rec(x, y, acc) {
return x < y ? rec(x + 1, y, concat(acc, x)) : acc;
}
return rec(x, y, []);
}
That isn't the most elegant solution though!
With recursion we can simply build up the stack with each recursive call. Each stack frame contains a computed partial result. Then we just need to unwind the stack and attach each partial result to an array:
const range = (x, y) => x < y ? [x].concat(range(x + 1, y)) : [];
Or more functional:
const concat = (xs, y) => xs.concat(y);
const range = (x, y) => x < y ? concat([x], range(x + 1, y)) : [];
Note that concat([x], range(x + 1, y)) is the recursive case and [] the base case.
Try this:
function rangeOfNumbers(startNum, endNum) {
if (startNum - endNum === 0) {
return [startNum];
} else {
const numbers = rangeOfNumbers(startNum + 1, endNum);
numbers.unshift(startNum);
return numbers;
}
};
Solution:
Solved this recursion problem, which is taking 2 numbers as input and returning back the array which contains range of the numbers inclusive of the startNumber and EndNumber
Assumption-> end_num is always greater than start_num
function rangeOfNumbers(start_num, end_num) {
if(start_num!==end_num){
let numbers = rangeOfNumbers(start_num+1,end_num);
numbers.unshift(start_num);
return numbers;
}
else
return [start_num];
};
Results will be always empty since you actually don't put anything in it.
What would work is this
function range(x,y){
var results = [];
if(x === y){
return results;
}
results.push(x);
return results.concat(range(x + 1,y));
}
range(1,5);
Let's firstly try to answer your "why" question before we give a solution because none of these answers explain your "why" question.
When you return results.push(<any argument>) the return value is the length of the array after the push. On the first call in your example, x does not equal y, so we are returning the call to push. You can think of it like this:
return array.push(<anything>) is going to be the same as:
array.push(<anything>)
return array.length
Therefore, you will always return the number 1 from this because the length of the array when you push the function call to it is 1. The content of that array will be another array that's nested all the way to the n levels deep where n is the range, but it's length will still be one and you will never see the content of this array unless you did it this way:
results.push(rangeOfNumbers(x+1, y))
return results;
In your example rangeOfNumbers(1, 5), if you logged that return value it would look like this:
[ [ [ [ [ ] ] ] ] ]
I solved it this way, but I like the functional solution that was posted by another user more:
function rangeOfNumbers(s, e) {
return s == e ? [s] : [s, ...rangeOfNumbers(s+1, e)];
}
function rangeOfNumbers(startNum, endNum) {
if (startNum>endNum){
return [];
}
else{
const range = rangeOfNumbers(startNum+1, endNum);
range.unshift(startNum);
return range;
}
};
//or more simple
function rangeOfNumbers(startNum, endNum) {
return endNum>=startNum?rangeOfNumbers(startNum,endNum-1).concat(endNum):[];
};
function rangeOfNumbers(firstNum, lastNum) {
if (firstNum - lastNum === 0) {
return [lastNum];
} else {
let rangeArray = rangeOfNumbers(firstNum, lastNum - 1);
rangeArray.push(lastNum);
return rangeArray;
}
}
console.log(rangeOfNumbers(1, 5))
Lots of clever solutions posted, but I think this is a use case for the plain old 'for loop'. It's easier to see what's happening, and it will be easier for new devs on your team. My example is inclusive (it will include the min value and the max value), and it has an optional step parameter which will default to 1 if not passed in.
function range(min, max, step = 1) {
let arr = []
for (let i = min; i <= max; i = i + step ) {
arr.push(i)
}
return arr
}

Categories