Reputation: 1102
Writing a recursion algorithm to get anagrams of a string.
Expected output for string abc
is ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']
However, what I get is [ 'abc', 'acb', 'ba', 'cab', 'cba' ]
.
I know where the issue is: When the recursion goes back to level 0 (meaning str==='abc' and i==1), substring works (exracts 'a'), but slice does not(does not extract c).
function anagramPermutationMemo(str, memo, resultsArr, level) {
if (!memo) {
memo = '';
resultsArr = [];
level = 0;
}
console.log('str -', str);
console.log('memo -', memo, '\n');
if (str.length === 0) {
resultsArr.push(memo);
return;
}
for (var i in str) {
console.log('level', level);
console.log('str', str);
console.log('i -', i, str[i]);
console.log('substring', str.substring(0, i));
console.log('slice', str.slice(i + 1));
var newStr = str.substring(0, i).concat(str.slice(i + 1));
console.log('newStr - ', newStr, '\n');
anagramPermutationMemo(newStr, memo + str[i], resultsArr, level + 1);
}
level -= 1;
console.log('backtrack', level,'\n');
return resultsArr;
};
Upvotes: 2
Views: 1257
Reputation: 1
I know where the issue is
The issue is for..in
loop.
Explanation:
variable
of for..in
loop is a String
, not a Number
. Within bracket notation
str[i]
expected result is returned, as property of object is a string, e.g., str["0"]
; Number
can also be used to get property of object within bracket notation str[0]
.
String.prototype.slice()
calls ToInteger
on parameters, though does not return expected result without casting string to number using sign
ToInteger
The abstract operation ToInteger converts its argument to an integral numeric value. This abstract operation functions as follows:
- Let number be the result of calling ToNumber on the input argument.
- If number is NaN, return +0.
- If number is +0, −0, +∞, or −∞, return number.
- Return the result of computing
sign
(number) ×floor
(abs(number)).
String.prototype.substring()
expects Number
as parameter.
If either argument is
NaN
or negative, it is replaced with zero; if either argument is larger than the length of the String, it is replaced with the length of the String.
Solution, substitute for
loop for for..in
loop
function anagramPermutationMemo(str, memo, resultsArr, level) {
if (!memo) {
memo = '';
resultsArr = [];
level = 0;
}
console.log('str -', str);
console.log('memo -', memo, '\n');
if (str.length === 0) {
resultsArr.push(memo);
return;
}
for (var i = 0; i < str.length; i++) {
console.log('level', level);
console.log('str', str);
console.log('i -', i, str[i]);
console.log('substring', str.substring(0, i));
console.log('slice', str.slice(i + 1));
var newStr = str.substring(0, i).concat(str.slice(i + 1));
console.log('newStr - ', newStr, '\n');
anagramPermutationMemo(newStr, memo + str[i], resultsArr, level + 1);
}
level -= 1;
console.log('backtrack', level, '\n');
return resultsArr;
};
var p = anagramPermutationMemo("abc");
console.log(p);
Solution:
Cast i
parameter to Number
using +
operator at calls to .substring()
, .slice()
when using for..in
loop, where parameters of methods are expecting Number
.
function anagramPermutationMemo(str, memo, resultsArr, level) {
if (!memo) {
memo = '';
resultsArr = [];
level = 0;
}
if (str.length === 0) {
resultsArr.push(memo);
return;
}
for (var i in str) {
var newStr = str.substring(0, +i).concat(str.slice(+i + 1));
var _newStr = str.substring(0, i).concat(str.slice(i + 1));
console.log(`newStr with string as parameter:${_newStr}`);
console.log(`newStr with number as parameter:${newStr}`);
console.log(`i:${str.substring(0, i)}, ${str.slice(i + 1)}`);
console.log(`+i:${str.substring(0, +i)}, ${str.slice(+i + 1)}`)
anagramPermutationMemo(newStr, memo + str[i], resultsArr, level + 1);
}
level -= 1;
return resultsArr;
};
Object.defineProperty(String, "abc", {
value:123,
enumerable:true
})
var p = anagramPermutationMemo("abc");
console.log(p);
Upvotes: 0
Reputation: 665122
It would help if you'd also log the value of i+1
(which you're passing to slice
). The problem with your for…in
enumeration is that it enumerates properties, which are strings. Your i
will be "0"
, "1"
and "2"
(and possibly other things if String.prototype
was extended with enumerable methods).
When you add 1 to that, it's doing string concatenation, and you'll end up with "01"
, "11"
and "21"
instead of the desired numbers. Given that slice
casts its arguments to numbers, "01"
actually works as desired, but the others will lead to indices after the end of the string, so you end up with omitting those parts entirely:
anagramPermutationMemo("abc".substring(0, "1")+"abc".slice("11"), ""+"abc"["1"], […], 0+1);
// ^^^^ should be 2
anagramPermutationMemo("a", "b", […], 1);
// ^ should be "ac"
To fix the issue, just use a standard for (var i=0; i<str.length; i++)
loop.
Upvotes: 1