Wrong exit condition in recursive solution - javascript

There are a 2D char-array and a search word. The task is to find out if there's the word in the array.
The word can be placed in the array in any curve with 4 directions: up, down, left, right.
You can't reuse the letters. For example, from an array [a, b, c] you can't get a word "ababc".
See the examples below.
function findWord(puzzle, word) {
}
const puzzle = [
'ANGULAR',
'REDNCAE',
'RFIDTCL',
'AGNEGSA',
'YTIRTSP',
];
console.log(findWord(puzzle, 'ANGULAR')); // true
console.log(findWord(puzzle, 'REACT')); // true
console.log(findWord(puzzle, 'ARRAY')); // true
console.log(findWord(puzzle, 'UNDEFINED')); // true
console.log(findWord(puzzle, 'RED')); // true
console.log(findWord(puzzle, 'STRING')); // true
console.log(findWord(puzzle, 'CLASS')); // true
console.log(findWord(puzzle, 'FUNCTION')); // false
console.log(findWord(puzzle, 'NULL')); // false
My solution:
function findWord(puzzle, word) {
puzzle = puzzle.map(e => e.split(""));
const current = [];
const clear = [];
for (let y = 0; y < puzzle.length; y++) {
for (let x = 0; x < puzzle[y].length; x++) {
if (puzzle[y][x] === word[0]) {
clear.push({x, y});
current.push(...getSurround(x, y, puzzle));
}
}
}
return nextTurn(puzzle, word.slice(1), clear, current);
}
function nextTurn(puzzle, word, clear, current) {
const next = [];
if (word.length === 0) return true;
for (let v of clear) {puzzle[v.y][v.x] = "-"}
clear.length = 0;
for (let v of current) {
if (v === null) continue;
if (v.letter === word[0]) {
clear.push({x: v.x, y: v.y});
next.push(...getSurround(v.x, v.y, puzzle));
}
}
if (next.length === 0) return false;
return nextTurn(puzzle, word.slice(1), clear, next);
}
function getSurround(x, y, puzzle) {
const surround = [];
const u = (y !== 0) ? {x, y: y - 1, letter: puzzle[y - 1][x]} : null;
const r = (x !== puzzle[y].length - 1) ? {x: x + 1, y, letter: puzzle[y][x + 1]} : null;
const d = (y !== puzzle.length - 1) ? {x, y: y + 1, letter: puzzle[y + 1][x]} : null;
const l = (x !== 0) ? {x: x - 1, y, letter: puzzle[y][x - 1]} : null;
surround.push(u, r, d, l);
return surround;
}
It seems that solution works for finding the word, but the problem is in the recursion exit.
I made a guess, that true is when the word is done, and false is when the word is not done and there is no other appropriate letter to end the word.

Your issue seems to be with:
for (let v of clear) {puzzle[v.y][v.x] = "-"}
clear.length = 0;
for the word "angular", this will set all "a"s to "-", which you shouldn't be doing, you should only be setting "a" to blank if/when you use it. Currently you're setting all "a" characters to "-" which means that you wont be able to use "a" again (so the second a in "angular" wont be found).
I suggest removing the idea of your clear array, and instead updating your nextTurn function:
function nextTurn(puzzle, word, current) {
if (word.length === 0) return true;
for (const v of current) {
if (v.letter === word[0]) {
const nextCandidates = getSurround(v.x, v.y, puzzle);
puzzle[v.y][v.x] = '-'; // mark letter as we're using it
const turnResult = nextTurn(puzzle, word.slice(1), nextCandidates);
if(turnResult) // recursive call returned true
return turnResult;
// If using the letter at x,y didn't work, "un-mark" it
puzzle[v.y][v.x] = v.letter;
}
}
return false;
}
The idea is to mark the current letter as "used" ("-") right before we recurse and call nextTurn(). This allows us to use the current letter right before we search its surrounding letters in the next call to nextTurn(). If searching that particular letter doesn't work, we backtrack and set the letter in the puzzle back to "available" by setting the letter back to its original value so that we can search the other possible options (and possible reuse this letter at some further point in the word).
In the below snippet I've also updated your findWord() function so that it converts your 2d puzzle array of letters to an array of vertices ({x, y, letter} objects), which can then be used by nextTurn() to calculate the next possible solution for each letter. Lastly, I've also updated your getSurround function to avoid the unneeded null values. This helps remove iterations over values which you know you won't be processing:
function findWord(puzzle, word) {
puzzle = puzzle.map(str => Array.from(str));
const candidates = puzzle.flatMap((row, y) => row.map((letter, x) => toVertex(x, y, letter)));
return nextTurn(puzzle, word, candidates);
}
function nextTurn(puzzle, word, current) {
if (word.length === 0) return true;
for (const v of current) {
if (v.letter === word[0]) {
const nextCandidates = getSurround(v.x, v.y, puzzle);
//console.log(v.y, v.x, puzzle[v.y]);
puzzle[v.y][v.x] = '-'; // mark letter as we're using it
const turnResult = nextTurn(puzzle, word.slice(1), nextCandidates);
if(turnResult) // recursive call returned true
return turnResult;
// If using the letter at x,y didn't work, "un-mark" it
puzzle[v.y][v.x] = v.letter;
}
}
return false;
}
function getSurround(x, y, puzzle) {
const surround = [];
if(y !== 0)
surround.push(toVertex(x, y-1, puzzle[y - 1][x]));
if(x !== puzzle[y].length - 1)
surround.push(toVertex(x+1, y, puzzle[y][x + 1]));
if(y !== puzzle.length - 1)
surround.push(toVertex(x, y+1, puzzle[y + 1][x]));
if(x !== 0)
surround.push(toVertex(x - 1, y, puzzle[y][x - 1]));
return surround;
}
function toVertex(x, y, letter) {
return {x, y, letter};
}
const puzzle = [
'ANGULAR',
'REDNCAE',
'RFIDTCL',
'AGNEGSA',
'YTIRTSP',
];
console.log('ANGULAR', findWord(puzzle, 'ANGULAR')); // true
console.log('REACT', findWord(puzzle, 'REACT')); // true
console.log('ARRAY', findWord(puzzle, 'ARRAY')); // true
console.log('UNDEFINED', findWord(puzzle, 'UNDEFINED')); // true
console.log('RED', findWord(puzzle, 'RED')); // true
console.log('STRING', findWord(puzzle, 'STRING')); // true
console.log('CLASS', findWord(puzzle, 'CLASS')); // true
console.log('FUNCTION', findWord(puzzle, 'FUNCTION')); // false
console.log('NULL', findWord(puzzle, 'NULL')); // false
console.log('RANER', findWord(puzzle, 'RANER')); // false (additional test to try re-use)

Related

Hidden tests keep failing

I have written this piece of code that returns an array of odd numbers or even numbers depending on which of the arguments are greater than each other.
const number_game = (x, y) => {
// Code here
let numbersArray = [];
if (typeof x === 'number' && typeof y === 'number') {
if (x > y) {
for (let i = y + 1; i < x; i++) {
if (i % 2 === 0) {
numbersArray.push(i);
}
}
}
if (y > x) {
for (let i = x + 1; i < y; i++) {
if (i % 2 === 1) {
numbersArray.push(i);
}
}
}
if (y === x) {
return numbersArray;
}
return numbersArray;
}
else {
return `${x} and ${y} should be numbers`
}
}
console.log(number_game(3,13));
I have tested it with possible cases and it works but it keeps failing a hidden test online saying "expected [ Array(9) ] to deeply equal [ Array(11) ] or expected [ Array(10) ] to deeply equal [ Array(11) ]". I have tried to tweak my solution in different ways but it still didn't work. I want to know what I am doing wrong and how I can correct it.
P.S: A search on deepEquality reveals that "The assert.deepEqual() method tests if two objects, and their child objects, are equal, using the == operator".
I just can't seem to point where the error is specifically.
Only thing I could do was simplify it a bit.
const number_game = (x, y) => {
var isOdd = x > y;
let numbersArray = [];
if (typeof x != "number" || typeof y != "number") {
return "${x} and ${y} should be numbers";
}
for (let i = (isOdd ? y : x) + 1; i < (isOdd ? x : y); i++) {
if (i % 2 === (isOdd ? 0 : 1)) {
numbersArray.push(i);
}
}
return numbersArray;
};
console.log(number_game(13, 3));
So after much inquisitiveness and research, I discovered that the problem specs and the test cases are in conflict. An input of (12, 0) should yield => [2,4,6,8,10] according to the problem specs but that result will return this error as a failed test "expected [ Array(9) ] to deeply equal [ Array(11) ] or expected [ Array(10) ] to deeply equal [ Array(11) ]". However, I observed that when I remove the + 1 from let i = x + 1 and y = x + 1 and I add the equal to sign to i <=x and i <=y termination conditions, the test passes. The adjustment, however, returns this result [ 0, 2, 4, 6, 8, 10 ] which is not correct with the problem specs. This is the code that passed the test:
const number_game = (x, y) => {
// Code here
let numbersArray = [];
if (typeof x === 'number' && typeof y === 'number') {
if (x > y) {
for (let i = y; i <= x; i++) {
if (i % 2 === 0) {
numbersArray.push(i);
}
}
}
else {
for (let i = x; i <= y; i++) {
if (i % 2 === 1) {
numbersArray.push(i);
}
}
}
return numbersArray;
}
else {
return `${x} and ${y} should be numbers`
}
}

Check for equal quantity of 2 characters in string

Hi I am trying to create a function in JS that takes a string and checks if there are an equal number of "x"s and "o"s in the string.
My code so far (not working):
const checkXo = (str) => {
const y = 0;
const z = 0;
for (let x = 0, x < str.length, x++) {
if (str.charAt(x) == "o") {
y++;
} else if (str.charAt(x) == "x") {
z++;
}
}
if (y === z) {
return true;
} else {
return false;
}
}
checkXo("xxoo");
const defines a constant, so you won't be able to change values of y and z. Instead, you should use var or let:
let y = 0;
let z = 0;
Consider to do it in a `functional' way:
const checkXo = (str, a, b) =>
str.split('').filter(s => s === a).length ===
str.split('').filter(s => s === b).length
test it with
checkXo('xxoo', 'x', 'o') // return true
checkXo('stackoverflow', 'x', 'o') // return false
Please note a single line of code can check for equal quantity of any characters of your choice 'a, 'b'.

in Java script Given two strings, find if they are one edit away from each other

can you help me to write a function in javascript to Given two strings, find if they are one edit away from each other example :
(pale, ple ) true
(pales, pale ) true
(pale, bale ) true
(pale, bake) false
(face, facts ) false
Can you try this function to check that string only differs by one edit.
function checkDifferntString(str1, str2) {
let diff = 0;
if (str1 === str2) return true; // equal return true
let lengthDiff = Math.abs(str1.length - str2.length)
if (lengthDiff > 1) return false; // checks length diff if > 2 return false
for (let i=0; (i<str1.length || i < str2.length);i++) {
if (diff > 1) return false; // diff greater than 1 return false
if (str1.charAt(i) !== str2.charAt(i)) diff++
}
if (diff <= 1) return true
else return false;
}
console.log(checkDifferntString("pale", "pale")) // true
console.log(checkDifferntString("pale", "pales")) // true
console.log(checkDifferntString("pales", "pale")) // true
console.log(checkDifferntString("pales", "bale")) // false
I hope it helps. Thanks!
Check this out.
I made a simple function that iterates through the given two strings and check if there's more than 1 difference (in terms of characters) between these strings, an optional argument cs to allow case sensitivity, by default it equals to false, so 'a' and 'A' are the same.
function isEditFrom(str1, str2, cs) {
var cs = cs || false, i = 0, diff = 2, len1 = str1.length, len2 = str2.length, l = (len1 > len2) ? len1: len2;
if(len1 !== 0 && len2 !== 0) {
if(cs === false) {
str1 = str1.toLowerCase();
str2 = str2.toLowerCase();
}
for(; i < l; i++) {
if(str1[i] !== str2[i]) {
if(--diff === 0) {
return false;
}
}
}
return true;
} else {
return false;
}
}
and now we call that function:
isEditFrom('Pale', 'bAle'); // returns True
isEditFrom('Pale', 'bAle', true); // returns False as we set the third argument to true enabling case sensitivity, 'a' != 'A'
isEditFrom('face', 'facts'); // returns False

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, {});
}

check if elements in array are consecutive --- javascript

I have an array as
arr = [1,2,3,4,6,7,8,9]
Now I want to check if the values in the array are consecutive.
Being more specific, I want this
First Check gives first and second element are consecutive and the next element is not consecutive then the algo must return the first element from where the consecutive number started
Like
First Check will give 1
Second Check will give 6
and so on...
Please help
Thanks in advance
/**
* Given an array of number, group algebraic sequences with d=1
* [1,2,5,4,8,11,14,13,12] => [[1,2],[4,5],[8],[11,12,13,14]]
*/
import {reduce, last} from 'lodash/fp';
export const groupSequences = (array) => (
reduce((result, value, index, collection) => {
if (value - collection[index - 1] === 1) {
const group = last(result);
group.push(value);
} else {
result.push([value]);
}
return result;
}, [])(array)
);
/**
* Given an array of number, group algebraic sequences with d=1
* [1,2,3,4,5,6] => true
* [1,2,4,5,6] => false
*/
const differenceAry = arr.slice(1).map(function(n, i) { return n - arr[i]; })
const isDifference= differenceAry.every(value => value == 1)
console.log(isDifference);
One sidenote is that you want to call it multiple times, so each call should know which array it's working on and what the previous offset in that array was. One thing you can do is to extend the native Array object. [Demo]
Array.prototype.nextCons = (function () {
var offset = 0; // remember the last offset
return function () {
var start = offset, len = this.length;
for (var i = start + 1; i < len; i++) {
if (this[i] !== this[i-1] + 1) {
break;
}
}
offset = i;
return this[start];
};
})();
Usage
var arr = [1,2,3,4,6,8,9];
arr.nextCons(); // 1
arr.nextCons(); // 6
arr.nextCons();​ // 8
Check if all numbers in array are consecutives:
Updated March 2022
const allConsecutives = (arr) =>{
if(arr.some(n=> typeof n !== "number" || Number.isNaN(n))) return false;
return arr.every((num, i)=> arr[i+1]-num === 1 || arr[i+1] === undefined)
}
pseudo code:
int count = 0
for i = 0 to array.length - 2
if {array[i + 1] - array[i] = 1 then
count+=1
return i
else count=0}
const array1 = [1,2,3];
const sum = array1.reduce((accumulator, currentValue) =>{
return accumulator + currentValue;
});
const max = Math.max(...array1);
maximum = max
if(sum == maximum * (maximum+1) /2) {
console.log(true);
} else {
console.log(false);
}

Categories