写一个方法,输入一个字符串,可以返回这个字符传中只出现过一次的字符,首先想到的就是利用Set对象的特性去重,实践下来发现我只能用Set对象获取重复的字符,没法获取不重复的字符
后来又想到可以使用Map对象同时捕捉重复字符和不重复的字符,具体实施方法如下:
利用Map()对象实现
map[key] = value 赋值方法
function singleLetter(str) {
let map = new Map();
for (let i = 0; i < str.length; i ++) {
if (!map[str[i]]) {
map[str[i]] = 1;
} else {
map[str[i]] ++;
}
}
console.log(str, map);
for (let i = 0; i < str.length; i ++) {
if (map[str[i]] && map[str[i]] == 1) {
console.log(str[i])
}
}
}
singleLetter('abcdasdla') // b, c, s, l
首先想到的就是利用map[key] = value这种方法进行赋值,然后遍历的时候发现不能使用for..of 或者foreach方法进行遍历,后来去MDN文档查看,发现给map对象添加键值对,需要用到map.set(key, value)方法,如果直接用map[key] = value这种方法赋值,其实是在给map对象设置属性,也就是map[property] = propertyValue,但是这种情况下是无法对map对象进行遍历的,还有可能会造成一定的混乱
wrong
let wrongMap = new Map()
wrongMap['bla'] = 'blaa'
wrongMap['bla2'] = 'blaaa2'
console.log(wrongMap) // Map { bla: 'blaa', bla2: 'blaaa2' }
...但是,这样做的话,它的行为会不符合预期:
wrongMap.has('bla') // false
wrongMap.delete('bla') // false
console.log(wrongMap) // Map { bla: 'blaa', bla2: 'blaaa2' }
right
let myMap = new Map()
myMap.set('bla','blaa')
myMap.set('bla2','blaa2')
console.log(myMap) // Map { 'bla' => 'blaa', 'bla2' => 'blaa2' }
myMap.has('bla') // true
myMap.delete('bla') // true
console.log(myMap) // Map { 'bla2' => 'blaa2' }
然后又重新使用正确的Map对象写了一个方法
map.set(key, value) 赋值方法
function singleLetter(str) {
let map = new Map();
for (let i = 0; i < str.length; i ++) {
if (!map.get(str[i])) {// 如果不存在,设为1
map.set(str[i], 1);
} else { // 如果存在设为2
map.set(str[i], 2);
}
}
console.log(str, map);
for (let [key, value) of map.entries()) {
if (value == 1) {
console.log(key)
}
}
}
singleLetter('abcdasdla') // b, c, s, l
使用String.prototype.replace()方法将重复的字符替换成‘’空串
function singleLetter(str) {
let string = str;
for (let i = 0; i < string.length; i ++) {
for (let j = 0; j < string.length; j ++) {
if (i != j && string[i] == string[j]) {
string = string.replace(new RegExp(string[i], 'g'), '').toString();
}
}
}
return string
}
console.log(singleLetter('abcdasdla')) // bcsl
使用这个方法的过程中,主要是利用正则表达式进行替换,正常使用正则的情况是string.replace(string[i], ''), 但是这样只能替换第一个字符,不能全局替换,后来我又使用string.replace(/string[i]/g, '')这种方法,发现不对,//中间匹配的只能是字符,不能是变量,然后去搜索RegExp文档,发现RegExp()可以同时传入变量和标志‘g'
还遇到一个问题就是出现了栈溢出的问题,出现了无限循环,后来发现replace()方法会返回一个新的字符串,但是不能改变原字符串
利用二进制逻辑运算
将字符转换成二进制进行运算,比如字符a的二进制是0001,字符b的二进制是0010,拿a(0001)和o(0000)做与运算,结果是0000(==0), 所以将a添加到字符串string中,依此类推
function singleLetter(str) {
let string = '';
let o = 0; // 用来记录所有的字母
for (let i = 0; i < str.length; i ++) {
let tr = transfer2Binary(str[i]);
if ((o & tr) == 0) { // 表示没有重复字符
o |= tr;
string += str[i]
} else {
string = string.replace(str[i], '').toString()
}
}
return string
}
// 将字符转换成二进制数
function transfer2Binary(char) {
let b = char.charCodeAt(); // 获取字符的ASCII码
let a = b - 97; // 97是a的ASCII码
return Math.pow(2, a);
}
console.log(singleLetter('abcdasdla')) // bcsl
比较过程如下
i = 0:
-------
str[i] = 'a'
tr = 2^0 = 0001
o = 0000
o & tr == 0 成立
o |= tr => o = 0001
string = 'a'
i = 1:
-------
str[i] = 'b'
tr = 2^1 = 0010
o = 0001
o & tr == 0 成立
o |= tr => o = 0011
string = 'ab'
i = 2:
-------
str[i] = 'c'
tr = 2^2 = 0100
o = 0111
o & tr == 0 成立
string = 'abc'
i = 3:
-------
str[i] = 'd'
tr = 2^3 = 1000
o = 0111
o & tr == 0 成立
o |= tr => o = 1111
string = 'abcd'
i = 4:
-------
str[i] = 'a'
tr = 2^0 = 0001
o = 1111
o & tr == 0 不成立
string = string.replace(str[i], '').toString() => string = 'bcd'
i = 5:
-------
str[i] = 's'
tr = 2^18 = 1000000000000000000(1后面18个0)
o = 1111
o & tr == 0 成立
o |= tr => o = 10000000000000001111
string = 'bcds'
i = 6:
-------
str[i] = 'd'
tr = 2^3 = 1000
o = 10000000000000001111
o & tr == 0 不成立
string = string.replace(str[i], '').toString() => string = 'bcs'
i = 7:
-------
str[i] = 'l'
tr = 2^11 = 100000000000(1后面11个0)
o = 1111
o & tr == 0 成立
o |= tr => o = 10000000100000001111
string = 'bcsl'
i = 8:
-------
str[i] = 'a'
tr = 2^3 = 0001
o = 10000000000000001111
o & tr == 0 不成立
string = string.replace(str[i], '').toString() => string = 'bcsl' // string不包含'a',所以返回原字符串