我创建了一个函数,将字符串中每个单词的第一个字符变成大写字母。它工作正常,只是索引为1的第二个字母->字符被忽略。
你能改正吗?有什么方法可以改进吗?
const str = "hello stackoverflow community";
function afterSpaceUpper(str){
let a = "";
for(let i = 0; i<str.length-1; i++){
if(i == 0 ){
a += str[i].toUpperCase();
} else if(str[i] === " "){
a += str[i+1].toUpperCase();
} else{
a += str[i+1]
}
}
return a;
}
console.log(afterSpaceUpper(str));
字符串
6条答案
按热度按时间hgqdbh6s1#
我已经清理了你的函数。让我们看看如何解决你的问题。
1.检查
str
是否为空/null
/undefined
;如果是,则返回空字符串;否则继续执行函数的其余部分。1.第一个字符大写:
str[0].toUpperCase();
1.然后循环遍历字符串。由于第一个字符不是空格,它将被添加到
new_str
中,但当遇到空格时,将以下字符添加为(str[i + 1].toUpperCase()
)。这解决了所有问题,也使您的代码更清晰,更具可读性。
字符串
4dc9hkyq2#
尽量避免在字符之间循环,并使用
indexOf()
在字符串中导航:字符串
也可以使用regexp:
型
还有一个基准:
型
型
dm7nw8vv3#
修复bug
您遇到的问题只是第一个显示出来的问题,一个一个错误。我将一步一步地修复它。
这是我们感兴趣的代码段:
字符串
给定字符串
"hello world"
,它返回"Hllo World"
。让我们自己运行一下算法:
i
是0,所以我们添加字符串的第一个字符(在x1中)。i
现在是1,所以我们跳过第一个分支。str[i]
是"e"
,所以我们跳过第二个分支。但是,现在你有a += str[i + 1]
;因为+ 1
,第二个字母被跳过。a
现在是"Hl"
。i
现在是2,所以我们跳过第一个分支。str[i]
是"l"
,所以我们跳过第二个分支。a += str[i + 1]
添加下一个字母,第二个"l"
。a
现在是"Hll"
。正如 * 克里斯蒂安·特赖纳 * 总结的那样,附加的字符是:
i = 0
->str[0]
,i = 1
->str[2]
,i = 2
->str[3]
。正如你可能看到的,有一个off-by-one错误。偏移量只会导致第二个字母的问题;之后,所有的字母都被偏移,从那里开始就很好了。
所以,让我们删除
+ 1
,看看我们得到了什么:“HelloWworl”。好的,空格被替换为大写字母,字符串的最后一个字母丢失了。
缺少的字母是因为当
i < str.length - 1
为false时循环停止。因此,当i
到达最后一个字母的索引时,它 * 等于 *str.length
并且条件为false。这在之前不是问题的原因是因为off-by-one问题。我们可以通过简单地将条件更改为i < str.length
来修复它。空格/重复是因为空格处理分支只是在找到的空格后面添加字符。它应该添加空格,在后面添加字符,然后递增
i
以说明已经处理了下一个字符。像这样:型
它以前工作,因为前一次迭代已经添加了空间,由于off-by-one问题。
输出现在是正确的:
"Hello World"
. Tada!上面还有一个bug:如果最后一个字符是一个空格,它将尝试读取字符串末尾以外的内容,导致TypeError。现在简单的解决方法是检查下一个字符是否存在,例如使用
a += str[i + 1]?.toUpperCase()
(可选链接)。我的首选方法是检测当前字母前 * 的空格:
型
或者,可替代地:
型
i == 0
分支可以与其他空间处理分支合并,因为它们做的是相同的事情:型
它可以进一步“简化”(代码量,不理解),因为事实上(假设,真的)字符串不会有负索引,所以
i == 0
检查可以通过翻转其他比较来删除:型
或
型
这最后一个“改进”被下一节中的更改所抵消(它完全消除了对它的需求)。
改进
使用 Sally loves Lightning 建议的改进,您可以在第二个字符上开始循环,这可以被认为是简化事情:
型
长度检查是为了处理空字符串,否则这个更改会崩溃。
结果与 Sally loves Lightning 给出的结果相同(除了对空字符串的处理):
型
不同的方法
所有这些都是非常“必要的”。我们可以使用函数式编程:
型
这将在空格上进行分割,Map到每个部分以删除第一个字母,然后用空格将它们连接在一起。大写部分通过默认为空字符串来处理空部分(其中有多个空格,或者字符串以空格开始或结束)。如果部分中的字符少于两个,切片将返回空字符串。
然后是正则表达式(最早由 Christian Punenzo Traina 提出)。
这是我推荐的一个:
/(?:^|\s)\s*./g
。它匹配字符串或空格的开头,任何后面的空格,然后是一个非空格字符(凭借已经匹配的空格);这是一种在这种情况下工作的假的look-behindAssert(空格只是空格)。
这对于在边缘情况下正确工作是必要的,例如前导空格或多个空格。
正如 Christian Crimenzo Traina 建议的那样,单词边界在空格之外的字符之后匹配。这可能是也可能不是不可接受的:“let's”->“Let'S”,“hi-lo”->“Hi-Lo”。
以下字符匹配边界后的任何非空格字符:
/\b[^\s]/g
。向后看Assert(如 * Alexandria Nenashev* 所建议的)有效,是所有Assert中最干净的,但没有得到广泛的支持。
以下字符串匹配字符串开头或空格后面的任何字符:
/(?<=^|\s)./g
。vlf7wbxs4#
正如注解中提到的,您的代码确实没有以最佳方式完成,但我已经修复了这个问题,索引为
1
的第二个字母不会被忽略。字符串
从第二个字符(索引为
1
)开始循环,并使用str[i - 1]
检查前面的字符是否为空格。并且,始终大写第一个字符。rslzwgfq5#
当然,
a
会比str少一个字符。循环从i = 0
到str.length - 2
,也就是把str.length - 1
字符附加到空字符串a
上。之所以是第二个字符,是因为你在索引0处添加了一个字符,作为
i+1
。然后在所有的子循环迭代中,你在索引i+1
处添加了这个字符。因此,在第二次迭代(i = 1
)中,你在索引i+1
(=2
)处添加了一个字符,这是第三个字符,跳过了索引1
处的第二个字符。一个快速的修复方法是从
i = -1
开始,将所有的i + 1
字符添加到a
。以下是修改后的代码。
字符串
fafcakar6#
请看一下,下面的代码片段将为所有用例提供最佳效果
字符串