javascript 忽略字符串的第二个字符

8nuwlpux  于 12个月前  发布在  Java
关注(0)|答案(6)|浏览(124)

我创建了一个函数,将字符串中每个单词的第一个字符变成大写字母。它工作正常,只是索引为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));

字符串

hgqdbh6s

hgqdbh6s1#

我已经清理了你的函数。让我们看看如何解决你的问题。
1.检查str是否为空/null/undefined;如果是,则返回空字符串;否则继续执行函数的其余部分。
1.第一个字符大写:str[0].toUpperCase();
1.然后循环遍历字符串。由于第一个字符不是空格,它将被添加到new_str中,但当遇到空格时,将以下字符添加为(str[i + 1].toUpperCase())。
这解决了所有问题,也使您的代码更清晰,更具可读性。

const str = "hello stackoverflow community";

function afterSpaceUpper(str) {
  let new_str = "";
  if (!str) return "";

  new_str += str[0].toUpperCase();
  for (let i = 0; i < str.length - 1; i++) {
    if (str[i] === " ") {
      new_str += str[i + 1].toUpperCase();
      continue;
    }

    new_str += str[i + 1];
  }

  return new_str;
}

console.log(afterSpaceUpper(str));

字符串

4dc9hkyq

4dc9hkyq2#

尽量避免在字符之间循环,并使用indexOf()在字符串中导航:

const str = "hello stackoverflow community";

function afterSpaceUpper(str){

  if(!str.length){
    return '';
  }  

  let a = '', prev = 0, idx;
  do {
    a += str[prev++].toUpperCase()
    // find the next space, avoid wasted looping
    idx = str.indexOf(' ', prev);
    // add the whole word without looping with the closing space if exists
    a += str.slice(prev, idx === -1 ? str.length : idx + 1);
    // find the space from this position the next cycle
    prev = idx + 1; 
  } while (idx >= 0);
  return a;
  
}
[str, '', ' ', 'a', 'a b'].forEach(str => console.log(JSON.stringify(afterSpaceUpper(str))));

字符串
也可以使用regexp:

const str = "hello stackoverflow community";

console.log(str.replace(/(?<=^|\s)./g, s => s.toUpperCase()));


还有一个基准:

` Chrome/119
-----------------------------------------------------------------------
Alexander                   1.00x  |  x1000000   78   79   80   81   92
Darryl Noakes               1.83x  |  x1000000  143  145  146  148  148
original (fixed by Sally)   2.32x  |  x1000000  181  181  185  187  196
Alexander regexp            2.47x  |  x1000000  193  197  199  200  205
Wasit Shafi                 2.60x  |  x1000000  203  204  205  206  224
Christian regexp            4.45x  |  x1000000  347  353  357  358  358
-----------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

<script benchmark data-count="1000000">

const str = "hello stackoverflow community";

// @benchmark Darryl Noakes
function afterSpaceUpper4(str) {
  return str
    .split(" ")
    .map((part) => (part[0]?.toUpperCase() ?? "") + part.slice(1))
    .join(" ");
}

// @run
afterSpaceUpper4(str);

// @benchmark original (fixed by Sally)

function afterSpaceUpper3(str) {
    let a = str[0].toUpperCase(); // Start with the first character capitalized

    for (let i = 1; i < str.length; i++) {
        if (str[i - 1] === " ") {
            // Check the previous character
            a += str[i].toUpperCase();
        } else {
            a += str[i];
        }
    }

    return a;
}

// @run
afterSpaceUpper3(str);

// @benchmark Wasit Shafi
function afterSpaceUpper2(str) {
    let a = '';

    for (let i = 0; i < str.length; i++) {
        if (
            ((str[i] > 'a' && str[i] < 'z') || (str[i] > 'A' && str[i] < 'Z')) &&
            (i == 0 || str[i - 1] == ' ')
        ) {
            a += str[i].toUpperCase();
        } else {
            a += str[i];
        }
    }

    return a;
}

// @run
afterSpaceUpper2(str);

// @benchmark Christian regexp
const regexp2 = /(\b|\s)\w/g;

// @run
str.replace(regexp2, (x) => x.toUpperCase());

// @benchmark Alexander regexp
const regexp = /^|\s./g;

// @run
str.replace(regexp, s => s.toUpperCase())

// @benchmark Alexander
function afterSpaceUpper(str){
  if(!str.length){
    return '';
  }  
  let a = '', prev = 0, idx;
  do {
    a += str[prev++].toUpperCase();
    idx = str.indexOf(' ', prev);
    a += str.slice(prev, idx === -1 ? str.length : idx + 1);
    prev = idx + 1;
  } while (idx >= 0);
  return a;
  
}
// @run
afterSpaceUpper(str);

</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>

dm7nw8vv

dm7nw8vv3#

修复bug

您遇到的问题只是第一个显示出来的问题,一个一个错误。我将一步一步地修复它。
这是我们感兴趣的代码段:

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];
  }
}

字符串
给定字符串"hello world",它返回"Hllo World"
让我们自己运行一下算法:

  1. i是0,所以我们添加字符串的第一个字符(在x1中)。
  2. i现在是1,所以我们跳过第一个分支。str[i]"e",所以我们跳过第二个分支。但是,现在你有a += str[i + 1];因为+ 1,第二个字母被跳过。a现在是"Hl"
  3. 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以说明已经处理了下一个字符。像这样:
a += " ";
a += str[i + 1].toUpperCase();
i += 1;


它以前工作,因为前一次迭代已经添加了空间,由于off-by-one问题。
输出现在是正确的:"Hello World". Tada!
上面还有一个bug:如果最后一个字符是一个空格,它将尝试读取字符串末尾以外的内容,导致TypeError。现在简单的解决方法是检查下一个字符是否存在,例如使用a += str[i + 1]?.toUpperCase()(可选链接)。
我的首选方法是检测当前字母前 * 的空格:

if (i == 0) {
  a += str[i].toUpperCase();
} else if (str[i - 1] === " ") {
  a += str[i].toUpperCase();
} else {
  a += str[i];
}


或者,可替代地:

if (i == 0) {
  a += str[i].toUpperCase();
} else {
  a += str[i - 1] === " " ? str[i].toUpperCase() : str[i];
}


i == 0分支可以与其他空间处理分支合并,因为它们做的是相同的事情:

if (i == 0 || str[i - 1] === " ") {
  a += str[i].toUpperCase();
} else {
  a += str[i];
}


它可以进一步“简化”(代码量,不理解),因为事实上(假设,真的)字符串不会有负索引,所以i == 0检查可以通过翻转其他比较来删除:

// If i == 0, then str[i - 1] == undefined, which is not equal to " ".
if (str[i - 1] !== " ") {
  a += str[i];
} else {
  a += str[i].toUpperCase();
}


a += str[i - 1] !== " " ? str[i] : str[i].toUpperCase()


这最后一个“改进”被下一节中的更改所抵消(它完全消除了对它的需求)。

改进

使用 Sally loves Lightning 建议的改进,您可以在第二个字符上开始循环,这可以被认为是简化事情:

function afterSpaceUpper(str) {
  if (!str.length) return "";

  let a = str[0].toUpperCase();

  for (let i = 1; i < str.length; i++) {
    if (str[i - 1] === " ") {
      a += str[i].toUpperCase();
    } else {
      a += str[i];
    }

    // Or, alternatively:
    // a += str[i - 1] === " " ? str[i].toUpperCase() : str[i];
  }

  return a;
}


长度检查是为了处理空字符串,否则这个更改会崩溃。
结果与 Sally loves Lightning 给出的结果相同(除了对空字符串的处理):

const str = "hello stackoverflow community";

function afterSpaceUpper(str) {
  if (!str.length) return "";

  let a = str[0].toUpperCase();

  for (let i = 1; i < str.length; i++) {
    if (str[i - 1] === " ") {
      a += str[i].toUpperCase();
    } else {
      a += str[i];
    }

    // Or, alternatively:
    // a += str[i - 1] === " " ? str[i].toUpperCase() : str[i];
  }

  return a;
}

console.log(afterSpaceUpper(str));

不同的方法

所有这些都是非常“必要的”。我们可以使用函数式编程:

function afterSpaceUpper(str) {
  return str
    .split(" ")
    .map((part) => (part[0]?.toUpperCase() ?? "") + part.slice(1))
    .join(" ");
}

console.log(afterSpaceUpper("hello stackoverflow community"));


这将在空格上进行分割,Map到每个部分以删除第一个字母,然后用空格将它们连接在一起。大写部分通过默认为空字符串来处理空部分(其中有多个空格,或者字符串以空格开始或结束)。如果部分中的字符少于两个,切片将返回空字符串。
然后是正则表达式(最早由 Christian Punenzo Traina 提出)。
这是我推荐的一个:/(?:^|\s)\s*./g
它匹配字符串或空格的开头,任何后面的空格,然后是一个非空格字符(凭借已经匹配的空格);这是一种在这种情况下工作的假的look-behindAssert(空格只是空格)。

function afterSpaceUpper(str) {
  return str.replace(/(?:^|\s)\s*./g, (x) => x.toUpperCase());
}

console.log(afterSpaceUpper("hello stackoverflow community")});

这对于在边缘情况下正确工作是必要的,例如前导空格或多个空格。
正如 Christian Crimenzo Traina 建议的那样,单词边界在空格之外的字符之后匹配。这可能是也可能不是不可接受的:“let's”->“Let'S”,“hi-lo”->“Hi-Lo”。
以下字符匹配边界后的任何非空格字符:/\b[^\s]/g
向后看Assert(如 * Alexandria Nenashev* 所建议的)有效,是所有Assert中最干净的,但没有得到广泛的支持。
以下字符串匹配字符串开头或空格后面的任何字符:/(?<=^|\s)./g

vlf7wbxs

vlf7wbxs4#

正如注解中提到的,您的代码确实没有以最佳方式完成,但我已经修复了这个问题,索引为1的第二个字母不会被忽略。

const str = "hello stackoverflow community";

function afterSpaceUpper(str) {
    let a = str[0].toUpperCase(); // Start with the first character capitalized

    for (let i = 1; i < str.length; i++) {
        if (str[i - 1] === " ") {
            // Check the previous character
            a += str[i].toUpperCase();
        } else {
            a += str[i];
        }
    }

    return a;
}

console.log(afterSpaceUpper(str));

字符串
从第二个字符(索引为1)开始循环,并使用str[i - 1]检查前面的字符是否为空格。并且,始终大写第一个字符。

rslzwgfq

rslzwgfq5#

当然,a会比str少一个字符。循环从i = 0str.length - 2,也就是把str.length - 1字符附加到空字符串a上。
之所以是第二个字符,是因为你在索引0处添加了一个字符,作为i+1。然后在所有的子循环迭代中,你在索引i+1处添加了这个字符。因此,在第二次迭代(i = 1)中,你在索引i+1(= 2)处添加了一个字符,这是第三个字符,跳过了索引1处的第二个字符。
一个快速的修复方法是从i = -1开始,将所有的i + 1字符添加到a
以下是修改后的代码。

const str = "hello stackoverflow community";

function afterSpaceUpper(str){
  
  let a = "";
  
  for(let i = -1; i<str.length-1; i++){     // Start at i = -1
    
    if(i == -1 ){                           // Check first iteration
      a += str[i+1].toUpperCase();          // Capitalize first character
    } else if(str[i] === " "){
     a += str[i+1].toUpperCase();
    } else{
      a += str[i+1]
    }  
    
  }
  
  return a;
  
}

console.log(afterSpaceUpper(str));

字符串

fafcakar

fafcakar6#

请看一下,下面的代码片段将为所有用例提供最佳效果

const str = "hello stackoverflow community";

function afterSpaceUpper(str) {
    let a = '';

    for (let i = 0; i < str.length; i++) {
        // CONDITION 1: str[i] is always from a-z so that we can perform operation lowercase to uppercase
      
        // CONDITION 2:  if any of the below condition is true then do transformation lowercase to uppercase (even we have also check if the character is already in uppercase then no need to perform operation )
      
        // as per you use case we only have to transform character just after space so have to check if str[i] === ' '
        // edge case if the input string start with any character a-z so for that check i == 0
        if (
            (str[i] > 'a' && str[i] < 'z') &&
            (i == 0 || str[i - 1] == ' ')
        ) {

            a += str[i].toUpperCase();
        } else {
            a += str[i];
        }
    }

    return a;
}

console.log(afterSpaceUpper(str));

字符串

相关问题