Expanding use of Javascript array.reduce helper method - javascript

Background
I am following a course on Udemy which goes over all of the ES6 features. In one of the lessons the instructor talks about using the reduce helper method to solve the popular balance parenthesis interview question.
I can solve this without the reduce method. Although with the reduce method it does get the job done in less code. I have been asked to find the depth of the parenthesis in an interview before and was wondering if this could all be done in the same method using reduce.
I do not know why this addition to the question confuses me so much but I would like to learn.
Problem
I have been trying to figure it out for a while and it might be my lack of understanding how reduce works.
Example
This uses reduce to return true of false regarding if the parenthesis or open and closed evenly.
function balanceParens(string) {
return !string.split("").reduce((counter, char) => {
// Handle if parens open and close out of order
if (counter < 0) { return counter; }
// Add 1 for each open in order
if (char === "(") { return ++counter; }
// subtract 1 for each close in order
if (char === ")") { return --counter; }
// handle use case if char is not a paren
return counter;
}, 0);
}
console.log(balanceParens("((()))"));
Question
How would I return the max depth of the parenthesis using the reduce helper method.

You could maintain current depth and max depth while reducing.
function maxDepth(string) {
return string.split("").reduce(({current, max}, char) => {
// Handle if parens open and close out of order
if (current < 0) return {current, max}
// Add 1 for each open in order
if (char === "(") return { current: current + 1, max: Math.max(max, current + 1)}
// subtract 1 for each close in order
if (char === ")") return { current: current - 1, max}
return {current, max}
}, {current: 0, max: 0}).max;
}
console.log(maxDepth("(((()))(((())))()(((((()))))))"));

Here is a compact version that returns NaN when the parentheses are not balanced. It uses nested functions in a functional style:
function maxDepth(string) {
return ( ([depth, max]) => depth ? NaN : max )
([...string].reduce(([depth, max], ch) =>
(newDepth => [newDepth, newDepth < 0 ? NaN : Math.max(max, newDepth)])
(depth + (ch === "(") - (ch === ")"))
, [0, 0]));
}
console.log(maxDepth("(((()))(((())))()(((((()))))))"));

This should answer it!
function balanceParens(string) {
let max = 0;
let res = string.split("").reduce((counter, char) => {
// Handle if parens open and close out of order
if (counter < 0) {
return counter;
}
// Add 1 for each open in order
if (char === "(") {
if(++counter > max) {
max = counter;
}
return counter;
}
// subtract 1 for each close in order
if (char === ")") {
return --counter;
}
// handle use case if char is not a paren
return counter;
}, 0);
console.log("Max depth was :", max);
return !res;
}
console.log(balanceParens("((()(((())))))((((()))))"));

Related

How to check for valid braces in javascript, programming problem?

have been struggling for the last couple of days with the following problem from codewars:
Write a function that takes a string of braces, and determines if the order of the braces is valid. It should return  true  if the string is valid, and  false  if it's invalid.
All input strings will be nonempty, and will only consist of parentheses, brackets and curly braces:  ()[]{} .
What is considered Valid?
A string of braces is considered valid if all braces are matched with the correct brace.
Examples
"(){}[]" => True
"([{}])" => True
"(}" => False
"[(])" => False
"[({})](]" => False
So I'm really stuck with the code for this one, and this is what I have up to this point:
function validBraces(braces){
let opening = [ '(', '[', '{']
let closing = [ ')', ']', '}']
let count = 0
const left = []
const right = []
// I generate left and right arrays, left w/the opening braces from the input, right w/ the closing
for (let i = 0; i < braces.length; i++) {
if (opening.includes(braces[i])) {
left.push(braces[i])
} else if (closing.includes(braces[i])) {
right.push(braces[i])
}
}
if (braces.length % 2 !== 0) {
return false
}
// I know there's no point in doing this but at one point I thought I was finishing the program and thought I would 'optimize it' to exit early, probably this is dumb haha.
if (left.length !== right.length) {
return false
}
// The juicy (not juicy) part where I check if the braces make sense
for (let i = 0; i < left.length; i++) {
// If the list are made up of braces like ()[]{} add one to counter
if (opening.indexOf(left[i]) === closing.indexOf(right[i])) {
count += 1
} else // If left and right are mirrored add one to the counter
if (opening.indexOf(left[i]) === closing.indexOf(right.reverse()[i])) {
count += 1
}
}
//If the counter makes sense return true
if (count === braces.length / 2) {
return true
} else { return false}
}
console.log(validBraces( "()" )) //true
console.log(validBraces("([])")) //true
console.log(validBraces( "[(])" )) //false
console.log(validBraces( "[(})" )) //false
console.log(validBraces( "[([[]])]" )) //true
Some comments: I know I'm still not checking for this example ([])() but I thought of breaking this up into two smaller checks in some way.
Thank you if you read up to this point. I would appreciate guidance in some way, though I don't want the problem solved for me. I'm probably overcomplicating this in some way since its a 6kyu problem, if so a tip on how to approach it more cleverly would be very much appreciated.
Thank you in advance! :pray: :pray:
Hell yeah!! I'm very happy to finally reach to the solution myself using some of the hints given to me here:
function validBraces(braces){
let opening = [ '(', '[', '{']
let closing = [ ')', ']', '}']
let arr = []
//console.log(closing.indexOf(braces[")"]) === opening.indexOf(arr[")"]))
for (let i = 0; i < braces.length; i++) {
if (opening.includes(braces[i])) {
arr.push(braces[i])
} else
if (closing.indexOf(braces[i]) === opening.indexOf(arr[arr.length - 1])) {
arr.pop()
} else return false
} return arr.length === 0;
}
I was clearly overthinking it in the first place haha. Thanks for everyone that helped!
As Dave suggested, using a stack, I've wrote the code for it:
var leftBraces="([{";
var rightBraces=")]}";
function checkBraces(braces) {
var ok=true;
var stack=[];
for(var i=0; i<braces.length && ok; i++) {
var brace=braces[i];
if(leftBraces.includes(brace)) stack.push(brace);
else {
var leftBrace=stack.pop();
if(leftBrace==undefined) ok=false;
else if(leftBraces.indexOf(leftBrace)!=rightBraces.indexOf(brace)) ok=false;
}
}
if(stack.length) ok=false;
return ok;
}
Code assumes only braces (no spaces or other characters).
I'm using string.indexOf() that matches for leftBraces and rightBraces.
Also, within the for loop, notice the termination part (2nd): i<braces.length && ok - doesn't "have to" use the iterator and, if I'm not mistaken, can even be empty...
var validBraces = (s) => {
let objO = {'(': 0, '[': 1, '{': 2};
let objC = {')': 0, ']': 1, '}': 2};
let stack = [];
for (let i=0; i<s.length; i++) {
if (objO.hasOwnProperty(s[i])) {
if (stack.length === 0 || stack[stack.length-1].idx!==objO[s[i]])
stack.push({idx: objO[s[i]], count: 1});
else
stack[stack.length-1].count++;
}
else if (objC.hasOwnProperty(s[i])) {
if (stack.length === 0 || stack[stack.length-1].idx!==objC[s[i]])
return false;
else {
stack[stack.length-1].count--;
if (stack[stack.length-1].count===0)
stack.pop();
}
}
}
return stack.length === 0;
};
console.log(validBraces("(){}[]"));
console.log(validBraces("([{}])"));
console.log(validBraces("(})"));
console.log(validBraces("[(])"));
console.log(validBraces("[({})](]"));
Here is a simplified solution:
let isMatchingBraces = function(str) {
let stack = [];
let symbol = {
'(': ')',
'[': ']',
'{': '}'
};
for (let i = 0; i < str.length; i += 1) {
// If character is an opening brace add it to a stack
if (str[i] === '(' || str[i] === '{' || str[i] === '[') {
stack.push(str[i]);
}
// If that character is a closing brace, pop from the stack, which will also reduce the length of the stack each time a closing bracket is encountered.
else {
let last = stack.pop();
//If the popped element from the stack, which is the last opening brace doesn’t match the corresponding closing brace in the symbol, then return false
if (str[i] !== symbol[last]) {
return false
};
}
}
// After checking all the brackets of the str, at the end, the stack is not
// empty then fail
if (stack.length !== 0) {
return false
};
return true;
}
function validBraces(braces){
let par =0;
let bra =0;
let cur =0;
for(let i =0; i<braces.length; i++){
if(braces[i]==="("){
par++;
}
if(braces[i]===")"){
par--;
}
if(braces[i]==="["){
bra++;
}
if(braces[i]==="]"){
bra--;
}
if(braces[i]==="{"){
cur++;
}
if(braces[i]==="}"){
cur--;
}
}
if(par<0 || bra<0 || cur<0){
return false;
}
return true;
};
Here is my solution:
var isValid = function (s) {
let charMap = new Map();
for (let i = 0; i < s.length; i++) {
charMap.set(s[i], i);
}
return Boolean(
charMap.get("(") < charMap.get(")") &&
charMap.get("(") % 2 != charMap.get(")") % 2 &&
charMap.get("{") < charMap.get("}") &&
charMap.get("{") % 2 != charMap.get("}") % 2 &&
charMap.get("[") < charMap.get("]") &&
charMap.get("[") % 2 != charMap.get("]") % 2
);
};
Explanation:
In order to achieve a quick and short solution, I have identified the common pattern of validity for opening/closing braces.
The common pattern for opening and closing braces' validity is that if say the opening(closing) stands at the even index in the string, the other one should be odd and vice versa. Example {}, {[]}, {()[]}, {[()]}.
Because we want to avoid a double loop for performance reasons, we are using a Hash Table via Map() to store the character and the index.
An alternative for getting the character's index would be using Array's find or another method, but that would end up in a second loop over the values which we want to avoid.
Finally, once the indexes of and the characters are stored in the charMap, we check whether or not the stored closing/opening characters' standing (odd/even) in the string is not equal, e.g. if '(' is odd the ')' should be even and vice versa.
We check this via the remainder (%) operator, i.e. a number's remainder of 2 is 0 if even.
Additionally, we need to check whether the order of braces is correct, e.g. if '(' is before '}';
The Boolean() function coerces the comparisons in the desired result.

Stuck with how to calculate a mode from an array in typescript

I am stuck with a problem where I am supposed to figure out a mode from an array in typescript.
I am using the program Visual Studio Code and I know that I need a for loop but I am unsure what I should loop through it. I also have to make sure that if the array is empty, the number that always shows up is 0 and if there is two integers that show up the same amount of times, that the smaller number (whether positive or negative) is the number that is put as the mode.
Currently I have this part of the code:
export let mode = (a: number[]): number => {
let mode: number;
mode = a[0];
if (a.length === 0) {
return 0;
}
for (let i = 1; i < a. length; i++) {
if ()
return mode;
};
I know that there needs to be an if statement after the for loop that changes the mode when necessary, but I am unsure beyond that.
As you indicated: your mode should be a function which:
accepts an array of numbers and should return the number with highest occurrence.
if there are two or more numbers that share the highest occurrence, then it should return the number with the least value.
if the given array is empty, it should return 0.
You could do this:
If given array is empty, return 0 right away. Else proceed with the rest of the steps.
Count the occurrences of each number in the array.
Sort the result of #2 by number of occurrence and value. Sort by highest occurrence, then lowest value.
Get the first of the items from #3.
Here's an implementation using reduce() to do step #2, and sort() to do step #3.
let mode = (numbers: number[]): number => {
if (numbers.length === 0) {
return 0;
}
const m = numbers.reduce((items, current) => {
const item = (items.length === 0) ? null : items.find((x) => x.value === current);
(item) ? item.occurrence++ : items.push({ value: current, occurrence: 1 });
return items;
}, [])
.sort((a, b) => {
if (a.occurrence < b.occurrence) {
return 1;
} else if (a.occurrence > b.occurrence || a.value < b.value) {
return -1;
} else {
return (a.value === b.value) ? 0 : 1;
}
});
return m[0].value;
}

Recursion check whether a series of operations yields a given number

In the book Eloquent JS in the section of recursion, a program was given:
Consider this puzzle: by starting from the number 1 and repeatedly
either adding 5 or multiplying by 3, an infinite amount of new numbers
can be produced. How would you write a function that, given a number,
tries to find a sequence of such additions and multiplications that
produce that number? For example, the number 13 could be reached by
first multiplying by 3 and then adding 5 twice, whereas the number 15
cannot be reached at all.
I have following program which look like checks it, but I don't know how to make it print he sequence.
function tester (value, key) {
if (value == key) {
return 1;
}
else if (value > key) {
return 0;
}
else {
if ( tester(value+5, key) || tester(value*3, key) ) {
return 1;
}
return 0;
}
}
Your version is a little odd to me, returning 1 or 0 rather than true or false. Are you mostly used to a language that conflates booleans with such integers? But it looks like it should work.
I would write it a bit differently. I generally prefer my recursion to count down to smaller inputs. You can write a simple function to test the values like this:
const m3a5 = (n) => n < 1
? false
: n == 1
? true
: m3a5(n - 5) || (n % 3 === 0 && m3a5(n / 3))
console.log(m3a5(13)) //=> true
console.log(m3a5(15)) //=> false
console.log(m3a5(18)) //=> true
This should be entirely equivalent to yours, modulo the boolean/int differences.
With this one, you can can then expand it in a fairly straightforward manner to allow you to capture the steps:
const m3a5 = (n, steps = []) => n < 1
? false
: n == 1
? steps
: m3a5(n - 5, ['+5'].concat(steps))
|| (n % 3 === 0 && m3a5(n / 3, ['*3'].concat(steps)))
console.log(m3a5(13)) //=> ['*3', '+5', '+5']
console.log(m3a5(15)) //=> false
console.log(m3a5(18)) //=> ['*3', '+5, '+5', '+5']
Note that this will show one possible path, not all of them. For instance ['+5', '*3'] is another possible result for m3a5(18), one which you would get by switching the main branch to
: (n % 3 === 0 && m3a5(n / 3, ['*3'].concat(steps)))
|| m3a5(n - 5, ['+5'].concat(steps))
But if you want all the paths, that would be significantly different code.
You could store the sequence and if found the calculation return the sequence.
function tester(key, value = 1, sequence = value) {
if (value > key) {
return false;
}
if (value === key) {
return sequence;
}
return tester(key, value + 5, '(' + sequence + ' + 5)')
|| tester(key, value * 3, sequence + ' * 3');
}
console.log(tester(15));
console.log(tester(11));
console.log(tester(24));
console.log(tester(37));

How to count number of ways to parenthesize boolean expression string to evaluate to desired result

Straight out of CTCI, 8.14: Given a boolean expression consisting of the symbols 0 (false), 1 (true), & (AND), | (OR), and ^(XOR), and a desired boolean result value result, implement a function to count the number of ways of parenthesizing the expression such that it evaluates to result.
I'm attempting a brute force approach that calculates every single possible combo, if matches desired result, add it to an array(combos) and return that result length. It seems to work for most expressions, but not the 2nd example given. What do I seem to be missing?
function countEval(s, goalBool, combos = []) {
// on first call make s into array since theyre easier to work with
if (!(s instanceof Array)) {
// and turn 1s and 0s into their bool equivalent
s = s.split('').map((item) => {
if (item === '1') {
return true;
} else if (item === '0'){
return false;
} else {
return item;
}
});
}
if (s.length === 1 && s[0] === goalBool) {
combos.push(s[0]); // can be anything really
} else {
for (let i = 0; i < s.length - 2; i = i + 2) {
// splice out the next 3 items
const args = s.splice(i, 3);
// pass them to see what they evaluate too
const result = evalHelper(args[0], args[1], args[2]);
// splice that result back in s array
s.splice(i, 0, result);
// pass that array to recurse
countEval(s, goalBool, combos);
// remove said item that was just put in
s.splice(i, 1);
// and reset array for next iteration
s.splice(i, 0, ...args);
}
}
return combos.length;
}
function evalHelper(a, op, b) {
if (op === '|') {
return a || b;
} else if (op === '&') {
return a && b;
} else if (op === '^') {
return a !== b;
}
}
With the 2 examples given it works for the first one, but not the second...
console.log(countEval('1^0|0|1', false)); // 2, correct
console.log(countEval('0&0&0&1^1|0', true)); // 30, should be 10!?!?!
The Bug
Your program is not taking into account overlap.
Example
Consider your program when s = '1|1|1|1'.
In one of the depth-first search iterations, your algorithm will make the reduction s = (1|1)|1|1. Then in a deeper recursive level in the same search, your algorithm will make the reduction s = (1|1)|(1|1). Now s is fully reduced, so you increment the length of combos.
In a different depth-first search iteration, your algorithm will first make the reduction s = 1|1|(1|1). Then in a deeper recursive level in the same search, your algorithm will make the reduction s = (1|1)|(1|1). Now s is fully reduced, so you increment the length of combos.
Notice that for both cases, s was parenthesized the same way, thus your program does not take into account overlap.
A Better Solution
A lot of times, when a problem is asking the number of ways something can be done, this is usually a big indicator that dynamic programming could be a potential solution. The recurrence relation to this problem is a bit tricky.
We just need to pick a "principle" operator, then determine the number of ways the left and right side could evaluate to true or false. Then, based on the "principle" operator and the goal boolean, we can derive a formula for the number of ways the expression could evaluate to the goal boolean given that the operator we picked was the "principle" operator.
Code
function ways(expr, res, i, j, cache, spaces) {
if (i == j) {
return parseInt(expr[i]) == res ? 1 : 0;
} else if (!([i, j, res] in cache)) {
var ans = 0;
for (var k = i + 1; k < j; k += 2) {
var op = expr[k];
var leftTrue = ways(expr, 1, i, k - 1, cache);
var leftFalse = ways(expr, 0, i, k - 1, cache);
var rightTrue = ways(expr, 1, k + 1, j, cache);
var rightFalse = ways(expr, 0, k + 1, j, cache);
if (op == '|') {
if (res) {
ans += leftTrue * rightTrue + leftTrue * rightFalse + leftFalse * rightTrue;
} else {
ans += leftFalse * rightFalse;
}
} else if (op == '^') {
if (res) {
ans += leftTrue * rightFalse + leftFalse * rightTrue;
} else {
ans += leftTrue * rightTrue + leftFalse * rightFalse;
}
} else if (op == '&') {
if (res) {
ans += leftTrue * rightTrue;
} else {
ans += leftFalse * rightFalse + leftTrue * rightFalse + leftFalse * rightTrue;
}
}
}
cache[[i, j, res]] = ans;
}
return cache[[i, j, res]];
}
function countEval(expr, res) {
return ways(expr, res ? 1 : 0, 0, expr.length - 1, {});
}

Array.reduce mutating state outside is not recommended?

Why is mutating variables outside .reduce() method considered bad practice? In the example below I am mutating a variable declared outside from inside the reduce method. Why is it not recommended to do so?
function balanceParens(string) {
let max = 0;
let res = string.split("").reduce((counter, char) => {
// Handle if parens open and close out of order
if (counter < 0) {
return counter;
}
// Add 1 for each open in order
if (char === "(") {
if(++counter > max) {
max = counter;
}
return counter;
}
// subtract 1 for each close in order
if (char === ")") {
return --counter;
}
// handle use case if char is not a paren
return counter;
}, 0);
console.log("Max depth was :", max);
return !res;
}
console.log(balanceParens("((()(((())))))((((()))))"));
Why is mutating variables outside .reduce() method considered bad practice?
Because you are mixing the functional with the imperative approach. Keep it to a single paradigm instead of confusing everyone.
You would go with
either reduce with a pure callback and no side effects
function step({max: oldMax, counter: oldCounter}, char) {
const counter = oldCounter + (char=="(") - (char==")");
const max = counter > oldMax ? counter : oldMax;
return {max, counter};
}
function maxParensLevel(string) {
assert(hasBalancedParens(string));
const {max, counter} = string.split("").reduce(step, {max:0, counter:0});
return max;
}
or a simple loop and two mutable variables
function maxParensLevel(string) {
assert(hasBalancedParens(string));
let max = 0;
let counter = 0;
for (const char of string.split("")) {
if (char == "(")
counter++;
else if (char == ")")
counter--;
if (counter > max)
max = counter;
}
return max;
}
but not both.
See #JordanRunning comment:
"If you do that, why bother using reduce at all? If your output is via side-effects you might as well be using forEach"
I was about to say the same. And here is a version of the function without an external dependency:
function balanceParens(string) {
let res = string.split("").reduce((state, char) => {
// Handle if parens open and close out of order
if (state.counter < 0) {
return state;
}
// Add 1 for each open in order
if (char === "(") {
if (++state.counter > state.max) {
state.max = state.counter;
}
return state;
}
// subtract 1 for each close in order
if (char === ")") {
state.counter = --state.counter;
return state;
}
// handle use case if char is not a paren
return state;
}, {max: 0, counter: 0});
console.log("Max depth was :", res.max);
return !res.counter;
}
console.log(balanceParens("((()(((())))))((((()))))"));

Categories