Soduko solving function takes forever with recursion - javascript

class SudokuSolver {
// check for string being sudoku
validate(puzzleString) {
const puzzleArr = puzzleString.split("")
const digitCheck = puzzleArr.every( val => val.match(/[1-9]/) || val.match(/\./))
const lengthCheck = puzzleArr.length == 81? true: false;
const checkString = this.checkString(puzzleString)
if(digitCheck && lengthCheck && checkString){
return "valid"
}
if(!lengthCheck){
return "Expected puzzle to be 81 characters long"
}
if(!digitCheck){
return "Invalid characters in puzzle"
}
if(!checkString){
return "Invalid puzzle string"
}
}
// check for string by digit.
checkString(puzzleString){
const puzzleArr = puzzleString.split("")
const check = puzzleArr.every((val, index) => {
let {row, column} = this.getRowColumn(index);
if(val.match(/\./)){
return true
}
if(val.match(/[1-9]/)){
column += ""
val = +val;
const rowCheck = this.checkRowPlacement(puzzleString, row, column, val)
const colCheck = this.checkColPlacement(puzzleString, row, column, val)
const sqrCheck = this.checkSquareRegionPlacement(puzzleString, row, column, val)
if(rowCheck && colCheck && sqrCheck){
return true
}
}
return false;
})
return check;
}
// check by place in array of string and returns row and column.
getRowColumn(place){
const value = +place
place += ""
let obj = {};
place.match(/\b(9|1[0-7])\b/)? obj = {row: 'B', column: value - 8}
: place.match(/1[8-9]|2[0-6]/)? obj = {row: 'C', column: value - 17}
: place.match(/2[7-9]|3[0-5]/)? obj = {row: 'D', column: value - 26}
: place.match(/3[6-9]|4[0-4]/)? obj = {row: 'E', column: value - 35}
: place.match(/4[5-9]|5[0-3]/)? obj = {row: 'F', column: value - 44}
: place.match(/5[4-9]|6[0-2]/)? obj = {row: 'G', column: value - 53}
: place.match(/6[3-9]|7[0-1]/)? obj = {row: 'H', column: value - 62}
: place.match(/7[2-9]|80/)? obj = {row: 'I', column: place - 71}
: obj = {row: 'A', column: value + 1};
return obj;
}
// check for valid inputs.
checkValues(row, column, value){
value += ""
column += ""
if(!value.match(/[1-9]/)){
return "Invalid value"
}
if(!row.match(/[A-I]/) || !column.match(/[1-9]/)){
return "Invalid coordinate"
}
return "fine"
}
// check for row(character) and return min and max value for that row.
rowAdd(row){
let arr;
switch(row){
case "A":
arr = [0, 9];
break;
case "B":
arr = [9, 18];
break;
case "C":
arr = [18, 27];
break;
case "D":
arr = [27, 36];
break;
case "E":
arr = [36, 45];
break;
case "F":
arr = [45, 54];
break;
case "G":
arr = [54, 63];
break;
case "H":
arr = [63, 72];
break;
case "I":
arr = [72, 81];
break;
}
return arr;
}
//check placement by row
checkRowPlacement(puzzleString, row, column, value) {
const [min, max] = this.rowAdd(row)
const index = min + parseInt(column) - 1
const puzzleArr = puzzleString.split("")
for(let i = min; i < max; i++){
if(puzzleArr[i] == value){
if(i == index){
continue
}
return false
}
}
return true;
}
//check placement by col
checkColPlacement(puzzleString, row, column, value) {
const puzzleArr = puzzleString.split("")
const startIndex = parseInt(column) - 1;
const index = this.rowAdd(row)[0] + parseInt(column) - 1;
for(let i = startIndex; i < 81; i+= 9){
if(puzzleArr[i] == value){
if(index == i){
continue
}
return false;
}
}
return true
}
// check for which square does the value belongs
checkSquareRegion(row, column){
column = +column;
switch(row){
case "A": case "B": case "C":
if(column < 4){
return "0"
}
if(column < 7){
return "1"
}
if(column < 10){
return "2"
}
;
case "D": case "E": case "F":
if(column < 4){
return "3"
}
if(column < 7){
return "4"
}
if(column < 10){
return "5"
}
;
case "G": case "H": case "I":
if(column < 4){
return "6"
}
if(column < 7){
return "7"
}
if(column < 10){
return "8"
}
;
default:
return undefined
}
}
// for square check of different values. return true if value does differ then others.
checkSquareRegionPlacement(puzzleString, row, column, value) {
const puzzleArr = puzzleString.split("")
const square = +this.checkSquareRegion(row,column)
const check = this.checkValues(row, column, value)
const index = this.rowAdd(row)[0] + parseInt(column) - 1;
if(check == "fine"){
let startIndex = (square * 3)
let flag = true;
for(let i = 0; i < 3; i++){
for(let j = startIndex; j < (startIndex + 3); j++){
if((parseInt(puzzleArr[j]) == value)){
if(puzzleArr[j] == puzzleArr[index]){
continue;
} else{
flag = false
}
} else{
continue;
}
}
if(flag == false){
break;
}
startIndex += 9;
}
if(flag){
return true
}
return false;
}else {
return check;
}
}
// solve whole puzzle
solve(puzzleString) {
const validate = this.validate(puzzleString)
if(validate == "valid"){
puzzleString = this.fillSoduko(puzzleString)
} else {
return {error: validate};
}
return puzzleString;
}
// fill soduko.
fillSoduko(puzzleString) {
const puzzleArr = puzzleString.split("")
var _this = this;
fill(puzzleArr.indexOf(val => !val.match(/[1-9]/)))
function fill(index){
console.log(index)
while (index < 81 && puzzleArr[index].match(/[1-9]/)) ++index; // skip non-empty cells
if (index == 81) return true; // we filled'em all, success!
let moves = getChoices(index);
for (let m of moves) {
puzzleArr[index] = m; // try one choice
if (fill(index + 1)) // if we can solve for the next cell
return true; // then return true, success!
}
puzzleArr[index] = "."; // no move worked; we failed, clear the cell
return false;
} // and backtrack
function getChoices(index) {
const {row, column} = _this.getRowColumn(index)
let choices = [];
for (let value = 1; value <= 9; ++value) {
if (_this.checkPlaceAndValue(puzzleString, row, column, value) == true) {
choices.push(value);
}
}
return choices;
}
return puzzleArr.join("")
}
// check for place and value of the value inserted.
checkPlaceAndValue(puzzleString, row, column, value){
value = +value;
const validate = this.validate(puzzleString);
const check = this.checkValues(row, column, value);
if((check == "fine") && (validate == "valid")){
const rowCheck = this.checkRowPlacement(puzzleString, row, column, value)
const colCheck = this.checkColPlacement(puzzleString, row, column, value)
const sqrCheck = this.checkSquareRegionPlacement(puzzleString, row, column, value)
if(rowCheck && colCheck && sqrCheck){
return true
} else{
let obj = {};
!rowCheck ? obj = {conflict: "row"}:
!colCheck ? obj = {conflict: "column"}:
obj = {conflict: "region"};
return obj;
}
} else{
let obj = {}
validate != "valid"? obj = {error: validate}:
obj = { error: check};
return obj;
}
}
}
module.exports = SudokuSolver;
So I have portion of code above which takes for ever to process. I have to use recursion as there was no other option. Please notify me if I am doing something wrong.
The backend thought about this class method is that it takes puzzle String and automatically fills it. It checks for empty(".") value in string and then check if any(1-9) is working for it. Ofcourse there are plenty of cells empty so we get not 1 but too many number as choice. We are then checking for each choice if it completes and validate a board or not.

The first thing I note is that this:
fill(puzzleArr.indexOf(val => !val.match(/[1-9]/)))
should be:
fill(puzzleArr.findIndex(val => !val.match(/[1-9]/)))
It's an easy mistake to make. indexOf is to find the first index in the array of the supplied value. findIndex is to find the first index in the array for which the supplied predicate function returns true.
On fixing that, I was able to spot that there is an error in checkSquareRegionPlacement. I haven't dug deeply into it, but using a fully solved puzzle and simply removing the final value, I tried to test a really easy case:
const complete = "172396845549287316368514297634975182927831564851462973215749638793658421486123759"
const puzzle = "17239684554928731636851429763497518292783156485146297321574963879365842148612375."
When we get into checkSquareRegionPlacement, we should be testing indices 60. 61, 62, 69, 70, 71, 78, 79, 80. But it was failing (by setting flag = false) when the index j was 25. I haven't dug in yet to see what's failing in this code, but that might at least point you in the right direction.
Overall, there are larger issues I would suggest you address:
There is a constant breaking apart of the puzzle string. You need a more useful internal format for the puzzle. Assuming that this 81-character string filled with digits (1 - 9) or dots (.) is your required input format, then I would suggest that on entering your initial function you change this to a more useful format, either a single array containing 81 characters / numbers or a 9 x 9 array of them. If you need at the very end to convert back to the string format, that's fine. In between the beginning of your initial call and its return, keep everything in a more useful format.
You repeatedly do the work of converting your plain array indices to the logical grid model and back. You could do this up front once with code something like this:
const _09 = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const _03 = [0, 1, 2];
const rows = _09 .map ((i) => _09 .map ((j) => 9 * i + j))
const cols = _09 .map ((i) => _09 .map ((j) => i + 9 * j))
const squares = _03 .flatMap ((i) => _03 .map ((j) => _03 .flatMap (
(k) => _03 .map (l => 27 * i + 3 * j + 9 * k + l)
)))
const groups = Array .from ({length: 80}, (_, i) => [
rows .find (row => row .includes (i)),
cols .find (col => col .includes (i)),
squares .find (square => square .includes (i))
])
then use those stored values. For instance, "Can I place a 7 at index 80?" becomes something like groups [80] .every (g => ! g .includes (7)) (untested!). This would stop much of the fiddling with rows and columns.
This is in a class for no obvious reason. Your methods are passing the relevant data between them, so there seems to be no internal state. It seems to me that this would be better as a module.
There are other issues, too. But overall, the issue seems to be that you're working too hard. I think a strong dose of simplification should really help.

Related

Recursive function to return each character in Input

i am trying to use recursion to return each character in a string. However, the output is not
//We define a function with input parameter.
function countCharInString(string) {
//vi Define an empty objec
const result = {};
//we loop through the length of string
for (let i = 0; i < string.length; i++) {
//create another variable for each element in string
const ch = string[i];
//BASE CASE: if string is empty, return Object with nothing
if (!result[ch]) {
return result[ch]=0;
} else {
//RECURSION: 1 plus whatever the length of the substring from the next character onwards is
return countCharInString(result[ch] + 1)
}
}
}
console.log(countCharInString("Vi skal tælle bogstaver"))
the output should be the following:
var result = {
l : 3,
a : 2,
e : 2,
s : 2,
t : 2,
v : 2,
b: 1,
i : 1,
k : 1,
o : 1,
r : 1,
æ : 1
};
i would suggest to do it with a simple reduce like so
var inputString = 'donald duck';
var result = inputString.split('').reduce((acc, char, index) => {
if (acc[char] !== undefined) {
acc[char] = acc[char] + 1;
}
else {
acc = { ...acc, [char]: 1 }
}
return acc
}, {})
see: https://jsfiddle.net/yswu91zh/21/
Only recursion would not give you the output that you are asking for. After recursively counting character you have to sort it by frequency and then by character. I have excluded a bunch of punctuation with space from counting, if you want exclude more just add it to the punctuation string. You have to use String.prototype.localeCompare() method to compare the characters. This method compares two strings in the current locale. As you are using Danish language you have to specify locale as da.
const punctuations = '.,:;!? ';
const countCharInString = (str, p = {}) => {
if (str.length === 0) return p;
const key = str[0].toLowerCase();
if (!punctuations.includes(key)) {
if (!p[key]) p[key] = 1;
else p[key] += 1;
}
return countCharInString(str.slice(1), p);
};
const cmp = (x, y) => {
if (x[1] === y[1]) {
return x[0].localeCompare(y[0], 'da');
}
return x[1] < y[1] ? 1 : -1;
};
const ret = Object.fromEntries(
Object.entries(countCharInString('Vi skal tælle bogstaver')).sort(cmp)
);
console.log(ret);

How to convert string to array with ranges JavaScript

Consider the following string: 7, 20, 22, 30–32, 33, 36–40, 46
I developed some code that will automatically parse said string into an array with the given ranges as follows.
Note: A typical use-case for this would be - to search for selected pages within a pdf with 100's of pages
var number_string = "7, 20, 22, 30–32, 33, 36–40, 46".toString().replace(/–/gi, '-').replace(/ /gi, '').split(',');
var new_arr = [];
$.each(number_string, function(index, value) {
if (value.match(/-/gi)) {
var range_arr = value.split('-');
var sub_arr = range(range_arr[0], range_arr[1]);
$.each(sub_arr, function(sub_index, sub_value) {
new_arr.push(parseInt(sub_value, 10));
});
} else {
new_arr.push(parseInt(value, 10));
}
});
console.log(new_arr);
function range(lowEnd, highEnd) {
var arr = [],
c = highEnd - lowEnd + 1;
while (c--) {
arr[c] = highEnd--
}
return arr;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Was there a more streamlined non jQuery method that could have been leveraged here that is also, simple, light weight and easy to read? Please no ES6 stuff as that is Greek to me.
Note: The ToInt function is just a function that returns a valid number or 0.
The jQuery.map() method acts like a flat map (returned sub arrays are flattend). In the map's callback function, use String.search() to check if there's a dash. If not convert to number with the + operator and return. If there's a dash, split, use a for loop to convert the min and max to an array, and return the array.
function convert(str) {
var arr = jQuery.map(str.split(', '), function(s) {
if(s.search('–') === -1) return +s;
var minmax = s.split('–');
var range = [];
for(var i = +minmax[0]; i <= +minmax[1]; i++) range.push(i);
return range;
});
return arr;
}
var number_string = "7, 20, 22, 30–32, 33, 36–40, 46";
var result = convert(number_string);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Using ESNext I would use Array.flatMap() instead of jQuery.map(), with String.inclues() to detect the dash, and Array.from() to generate the sub array.
const convert = (str) =>
str.split(', ')
.flatMap(s => {
if(!s.includes('–')) return +s;
const [min, max] = s.split('–');
return Array.from({ length: max - min + 1 }, (_, n) => n + +min);
});
var number_string = "7, 20, 22, 30–32, 33, 36–40, 46";
var result = convert(number_string, '–');
console.log(result);
I'd use .reduce. First split the initial string by commas. If the string being iterated over doesn't have -, then just push the number to the accumulator; otherwise, split by - to get a low and high number, then use a for loop to push all numbers from low to high to the accumulator:
const ToInt = Number;
const numArr = "7, 20, 22, 30–32, 33, 36–40, 46".split(', ');
const result = numArr.reduce((a, str) => {
if (!str.includes('–')) {
a.push(ToInt(str));
return a;
}
const [low, high] = str.split('–');
for (let i = Number(low); i <= high; i++) {
a.push(i);
}
return a;
}, []);
console.log(result);
If for some reason you don't want to use ES6, you can transform it to ES5 with Babel:
"use strict";
function _slicedToArray(arr, i) {
return (
_arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest()
);
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (
var _i = arr[Symbol.iterator](), _s;
!(_n = (_s = _i.next()).done);
_n = true
) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
var ToInt = Number;
var numArr = "7, 20, 22, 30–32, 33, 36–40, 46".split(", ");
var result = numArr.reduce(function(a, str) {
if (str.indexOf("–") === -1) {
a.push(ToInt(str));
return a;
}
var _str$split = str.split("–"),
_str$split2 = _slicedToArray(_str$split, 2),
low = _str$split2[0],
high = _str$split2[1];
for (var i = Number(low); i <= high; i++) {
a.push(i);
}
return a;
}, []);
console.log(result);
(but the ES6 version is more concise and probably a lot easier to read and understand)
I have my own implementation for this purpose
parseStringRange accepts strings like '1,2,3' and '1,2,3-5,6,7', also removes invalid chars (not numbers or , or -) and return one array with all numbers.
the function also returns the numbers ordered and removes duplicates, the user also can input unordered numbers even in range, '1-5' and '5-1' will return same output.
const parseStringRange = (range = '') =>
`${range}`
?.replace(/[^0-9,-]/g, '') //remove invalid chars
?.split(',') //convert 1,2 to [1, 2]
?.flatMap(s => { //convert 1-3 to [1,2,3]
if (!s.includes('-')) return [s]
const [min, max] = s.split('-').map(e => Number(e))
if (min > max) var i = -1
else var i = 1
const r = []
for (let p = min; p != max; p += i)
r.push(p)
r.push(max)
return r;
})
?.map(e => Number(e)) //convert all string numbers to number
?.reduce((t, e, i, a) => {
if (!t)
t = [... new Set(a)]
return t
}, null) //remove duplicates
?.sort((a, b) => a - b) //sort numbers, smallest first

Function that returns the sum of all numbers passed in arguments

I need help. Can you help me to solve this task.
Write the function that returns the summ of all numbers even string numbers that are passed as arguments and the amount of these numbers is unlimited.
If one of the parameters is an array, then
Sum is also added to the sum of the values of this array (if any of the
Values of this array is also an array, then the result is added
Also the sum of its values, and so on).
var sum = getSum (1, '1', 'one', [2, '2', 'two']);
console.log(sum);
I've tried to write something like this:
function getSum(){
var separeted = string.split(",");
var sum =0;
for(var i=0;i<arguments.length;i++){
sum += parseInt(arguments[i].toString()..match(/(\d+)/));
}
return sum;
}
var sum = getSum(1,"1","one",[2,'2', 'two']);
console.log(sum);
and here I've stacked. I think that I will need split and filter but how to combine that all in one function I can't find.
Thanks.
You could define a recursive function like so:
function getSum () {
return Array.from(arguments).reduce((sum, value) => {
if (Array.isArray(value)) {
sum += getSum.apply(this, value)
} else {
sum += Number(value)
}
return sum
}, 0)
}
var sum = getSum (1, '1', '3', [2, '2', '10']);
console.log(sum);
In order to account for string numbers, you must define a mapping from words to numbers and iteratively traverse the string of words to generate the number. Here's a possible implementation of that:
var stringToNumber = (function () {
const primary = new Map([
['zero', 0],
['one', 1],
['two', 2],
['three', 3],
['four', 4],
['five', 5],
['six', 6],
['seven', 7],
['eight', 8],
['nine', 9]
])
const secondary = new Map([
['ten', 10],
['eleven', 11],
['twelve', 12],
['thirteen', 13],
['fourteen', 14],
['fifteen', 15],
['sixteen', 16],
['seventeen', 17],
['eighteen', 18],
['nineteen', 19]
])
const prefix = new Map([
['twenty', 20],
['thirty', 30],
['forty', 40],
['fifty', 50],
['sixty', 60],
['seventy', 70],
['eighty', 80],
['ninety', 90]
])
const magnitude = new Map([
['hundred', 1e2],
['thousand', 1e3],
['million', 1e6],
['billion', 1e9],
['trillion', 1e12],
['quadrillion', 1e15],
['quintillion', 1e18],
['sextillion', 1e21],
['septillion', 1e24],
['octillion', 1e27],
['nonillion', 1e30],
['decillion', 1e33]
])
const types = { primary, secondary, prefix, magnitude }
class Parser {
static parse(word) {
if (isNaN(word)) {
const [type = null] = Object.keys(types)
.filter(type => types[type].has(word))
const value = types[type] ? types[type].get(word) : NaN
return { type, sign: 1, value }
} else {
const value = Math.abs(word)
const sign = Math.sign(word)
const [type = 'primary'] = Object.keys(types)
.filter(type => Array.from(types[type].values()).includes(value))
return { type, sign, value }
}
}
constructor() {
this.words = []
}
push(word) {
const parsed = Parser.parse(word)
if (parsed.type === null) {
return this.words.length
}
return this.words.push(parsed)
}
valueOf() {
if (this.words.length === 0) {
return NaN
}
const words = this.words
let total = 0
let { type: lastType, sign, value: run } = words[0]
let maxMagnitude = lastType === 'magnitude' ? run : 1
for (const { type, value } of words.slice(1)) {
switch (type) {
case 'magnitude':
if (value > maxMagnitude) {
run = (total + run) * value
total = 0
maxMagnitude = value
} else {
run *= value
}
break
case 'secondary':
case 'prefix':
switch (lastType) {
case 'magnitude':
total += run
run = value
break
case 'primary':
case 'secondary':
case 'prefix':
run = Number(String(run) + String(value))
}
break
case 'primary':
switch (lastType) {
case 'magnitude':
total += run
run = value
break
case 'prefix':
run += value
break
case 'primary':
case 'secondary':
run = Number(String(run) + String(value))
}
}
lastType = type
}
return sign * (total + run)
}
}
return function stringToNumber (string) {
const words = string
.trim()
.toLowerCase()
.split(/\s+/g)
const parser = new Parser()
for (const word of words) {
parser.push(word)
}
return parser.valueOf()
}
})()
function getSum () {
return Array.from(arguments).reduce((sum, value) => {
if (Array.isArray(value)) {
sum += getSum.apply(this, value)
} else if (!isNaN(value)) {
sum += Number(value)
} else {
sum += stringToNumber(String(value))
}
return sum
}, 0)
}
var sum = getSum (1, '1', 'one hundred thousand', [2, '2', 'twenty six hundred']);
console.log(sum);
Thanks to everyone I found the solution in the old "O'reilly Javascript by David Flanagan"
so as I thought in my loop I had to check if it's not null than change the element into Number()
Here is the code from the book
function flexisum(a) {
var total = 0;
for(var i = 0; i < arguments.length; i++) {
var element = arguments[i], n;
if (element == null) continue; // Ignore null & undefined
if (isArray(element)) // if argument is array
n = flexisum.apply(this, element); // count sum of all elements
else if (typeof element === "function") // if it's a function
n = Number(element()); // call and convert it to the 'number'
else n = Number(element); // or to convert it anyway
if (isNaN(n)) // If it was not possible to convert to a number, initiate exc.
throw Error("flexisum(): can't convert " + element +
"into the number");
total += n; // Otherwise, add n to the total
}
return total;
}
but in my case I've chaged it for my needs and took away the exc. so it could count sum without string words.
if (isNaN(n)) continue;
total += n;
}
So, thanks to everybody.
Books are the good sourse))))
i think need to iterate any number of label ,so you can use following method
function jsArrSum(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] == 'object')
sum += jsArrSum(arr[i]);
else
sum += arr[i];
}
return sum;
}
jsArrSum(array);

Finding a Single Integer in an array using Javascript

I was able to pull all single integers after 'reduce', but not working when there's all duplicates and output should be 0, not hitting my else or else if - code keeps outputting 0 vs the single integers
var singleNumber = function(nums) {
var sorted_array = nums.sort();
for (var i=0; i < sorted_array.length; i++){
var previous = sorted_array[i-1];
var next = sorted_array[i+1];
var singles = {key: 0};
var singlesArray = [];
if (sorted_array[i] !== previous && sorted_array[i] !== next){
singlesArray.push(sorted_array[i]);
singlesArray.reduce(function(singles, key){
singles.key = key;
//console.log('key', key);
return singles.key;
},{});
}
else if(singlesArray.length === 0) {
singles.key = 0;
return singles.key;
}
}
console.log('singles.key', singles.key);
return singles.key;
};
console.log(singleNumber([2,1,3,4,4]));
// tests
const n1 = [1,2,3,4,4] //[1,2,3]
const n2 = [1] //[1]
const n3 = [1,1] //0
const n4 = [1,1,1] //0
const n5 = [1,5,3,4,5] //[1,3,4]
const n6 = [1,2,3,4,5] //[1,2,3,4,5]
const n7 = [1,5,3,4,5,6,7,5] //[1,3,4,6,7]
const singleNumber = numbers => {
const reducer = (acc, val) => {
// check to see if we have this key
if (acc[val]) {
// yes, so we increment its value by one
acc[val] = acc[val] + 1
} else {
// no, so it's a new key and we assign 1 as default value
acc[val] = 1
}
// return the accumulator
return acc
}
// run the reducer to group the array into objects to track the count of array elements
const grouped = numbers.reduce(reducer, {})
const set = Object.keys(grouped)
// return only those keys where the value is 1, if it's not 1, we know its a duplicate
.filter(key => {
if (grouped[key] == 1) {
return true
}
})
// object.keys makes our keys strings, so we need run parseInt to convert the string back to integer
.map(key => parseInt(key))
// check to array length. If greater than zero, return the set. If it is zero, then all the values were duplicates
if (set.length == 0) {
return 0
} else {
// we return the set
return set
}
}
console.log(singleNumber(n7))
https://jsbin.com/sajibij/edit?js,console

Check for Anagram in 2 strings

I've created a function that checks if 2 words are anagrams, but I want to make it better. I feel the declaration of the counter after in the if statement is not quite well, if anybody have a better solution would be great.
function checkAnagram(string1, string2){
if(string1.length !== string2.length){
return false;
}
for(var i = 0; i < string1.length; i++){
if(count <= 0){
return false;
}
var count = 0;
for(var t = 0; t < string2.length; t++){
//counter = 0
if(string2[t].toLowerCase() == string1[i].toLowerCase()){
//counter++;
count++;
break;
}
}
}
return true;
}
Here is a much easier way of doing it:
var s1 = "test"
var s2 = "tset"
function testAnagram (s1, s2){
if(!s1 || !s2 || s1.length !== s2.length){return false;}
var lS1 = s1.toLowerCase();
var lS2 = s2.toLowerCase();
if(lS1 === lS2) {return false;}
var rS1 = lS1.split('').sort().join('');
var rS2 = lS2.split('').sort().join('');
return rS1 === rS2;
}
var result = testAnagram(s1, s2);
alert(result);
Your code returns true for strings 'aabb' and 'abcc', which are not anagrams. You can just sort the strings and check if they're equal:
function checkAnagram(string1, string2) {
return string1.toLowerCase().split("").sort().join("") === string2.toLowerCase().split("").sort().join("")
}
function checkAnagram(string1, string2) {
return string1.replace(/[^\w]/g,'').toLowerCase().split('').sort().join('') ===
string2.toLowerCase().replace(/[^\w]/g,'').split('').sort().join('')
}
If you don't want to use prebuild methods like .split() .sort() .join() then
function isAnagrams(stringA, stringB) {
// just to remove special char and convert it to lowercase
stringA = stringA.replace(/[^\w]/g, "").toLowerCase();
stringB = stringB.replace(/[^\w]/g, "").toLowerCase();
if (stringA.length !== stringB.length) return false;
let aCharMap = {};
let bCharMap = {};
/*
making of mapObject of both string, containing character as property and
count of that character as value
*/
for (let i = 0; i < stringA.length; i++) {
bCharMap[stringB[i]] = bCharMap[stringB[i]] + 1 || 1;
aCharMap[stringA[i]] = aCharMap[stringA[i]] + 1 || 1;
}
// checking both CharMap value
for (let q of stringB) {
if (bCharMap[q] !== aCharMap[q]) {
return false;
}
}
return true;
}
console.log(`isAnagram : ${isAnagrams('rail safety', 'fairy tales')}`)
so if you pass isAnagrams('rail safety','fairy tales'); then character map will look like this
aCharMap = { r: 1, a: 2, i: 1, l: 1, s: 1, f: 1, e: 1, t: 1, y: 1 }
bCharMap = { f: 1, a: 2, i: 1, r: 1, y: 1, t: 1, l: 1, e: 1, s: 1 }
then we will compare if they have same value or not based on that result will be decided
function isAnagram(str1, str2){
if(str1.length !== str2.length){
return false
}
let string1 = str1.toLowerCase().split('').sort().join('');
let string2 = str2.toLowerCase().split('').sort().join('');
return string1 === string2
}
isAnagram('abcdef', 'AbcFed')

Categories