JavaScript函数自动计算字符串中连续字母的数量

6
我正在尝试编写JavaScript函数 LetterCount 来计算字符串中连续字母的数量(而不是总数),但一直没有成功。
理想情况下: LetterCount("eeeeeoooohhoooee") = [["e", 5],["o",3],["h",2],["o",3],["e",2]]
以下代码仅在已知连续字母时才尝试计算字符串中连续字母的数量:
function LetterCount(str) {
for (var i=0; i<str.length;i++) {
    var arr1=[]; arr2=[]; arr3=[]; arr4=[]; arr5=[];
    var e=0; o=0; h=0; o2=0; e2=0;
    if(str[i]="e") {
        arr1 += "e";
        e++;
    }
    arr1.push(e);
    if(str[i]="o") {
        arr2 += "o";
        o++;
    }
    arr2.push(o);
    if(str[i]="h") {
        arr3 += "h";
        h++;
    }
    arr3.push(h);
    if(str[i]="o") {
        arr4 += "o";
        o2++;
    }
    arr4.push(o2);
    if(str[i]="e") {
        arr5 += "e";
        e2++;
    }
    arr5.push(e2);
}
return arr1.concat(arr2).concat(arr3).concat(arr4).concat(arr5);
}

在上面的代码中,我需要首先知道字符串中的字母是什么,以及它们有多少个,以什么顺序出现。
相反,你应该编写一个函数,能够自动识别字母本身,并返回连续字母的计数。如果答案格式如下,那就更好了:
 LetterCount("eeeeeoooohhoooee") = [["e", 5],["o",3],["h",2],["o",3],["e",2]]

非常感谢您的帮助!

6个回答

18

你可以使用正则表达式来匹配任何后面有零个或多个相同字母的字母。

rx=/([a-zA-Z])\1*/g;

你的示例匹配了["eeeee","oooo","hh","ooo","ee"]

使用map,在新数组中为每个索引返回初始字母和出现次数。

function letterCount(str) {
  var s = str.match(/([a-zA-Z])\1*/g) || [];
  return s.map(function(itm) {
    return [itm.charAt(0), itm.length];
  });
}

console.log(letterCount("eeeeeoooohhoooee"))

返回值:(数组)

[["e",5],["o",4],["h",2],["o",3],["e",2]]

注意事项:

  1. var s= str.match(/([a-zA-Z])\1/g)||[];*

返回匹配项(连续的字母)的数组或空数组([])。否则,如果字符串不包含任何字母,则会抛出错误(从调用null上的map)。

  1. \1*用于允许匹配单个字母的任意或没有连续重复。 '\1+'将无法匹配单个未重复的字母。

  2. 数组 map 期望一个函数,并传递三个参数- 每个索引处的值,索引号和对整个数组的引用。在这种情况下,仅使用每个索引的值,因此我们可以忽略其他参数。


我喜欢这个的简单性和使用正则表达式,因为这很可能会导致更快的处理速度。而且我以前从未使用过 map。比我的答案好多了 :) - Jonathan Gray
如果有什么可以改进的地方,那可能就是创建一个额外的封装,以便匿名函数不会在每次运行时声明。但即使如此,这也会削弱其简洁性。 - Jonathan Gray
谢谢你们俩提供这个漂亮的解决方案!第二行中匹配 ([a-zA-Z])\1*/g) 和 ([a-zA-Z]\1+/g) 有什么不同?在这种情况下,星号 * 不是用于前面的字符出现0次或多次吗?而在这种情况下应该是1次或多次,即与加号 + 一起使用? - Henry
"itm" 代表什么?第二行的意思是:如果没有找到匹配项,则 s 将是给定字符串上的匹配方法,或者是一个空数组?非常感谢您的澄清。 - Henry
我正在解释别人的代码,但是星号的使用是因为该算法针对连续零个或多个相同字符,为了匹配没有连续相同字符序列的情况。 语句 var s= str.match(/([a-zA-Z])\1*/g)||[]; 的基本含义是,如果 str.match(...) 返回假值,例如 0nullundefined ,则将一个空数组分配给 s,而不是分配 str.match 返回的值。 - Jonathan Gray
@Henry 抱歉我忘记标记你了.. 无论如何,在JavaScript中使用||的解释是它实际上不像一个“或”语句那样工作,而是会一直执行直到遇到一个真值(或者说非假值)才返回。如果找不到真值,它将返回false(或至少是一个假值)。另一方面,&&则完全按预期工作,并将返回true或false。 - Jonathan Gray

2

这是我的回答:

function LetterCount(str) {
    var current, i = 0, l = str.length;
    var outputArr = [];
    while(i<l) {
        current = str.charAt(i);
        if(!i++ || outputArr[outputArr.length-1][0] !== current)
            outputArr[outputArr.length] = [current, 1];
        else outputArr[outputArr.length-1][1]++;
        }
    return outputArr;
    }

作���对kennebec(非常棒的)答案的修改,使匿名函数不会在每次调用父函数时声明。这仅是为了参考更好的编程实践,与纯粹简单相比(这可能是最有效的方法):
var letterCount = (function(){
    var r = /([A-z])\1*/g,
        f = function(itm){
        return [itm.charAt(0), itm.length];
        };
    return function letterCount(str){
        return str.match(r).map(f);
        };
    }());

2
实际上,将["o",3]修正为["o",4] ;)
// node v0.10.31
// assert v1.3.0

var assert = require('assert');

function letterCount(str) {
    var i = 0,
        seq = 0,
        results = [];

    while (i < str.length) {
        var current = str[i],
            next = str[i + 1];

        if (typeof results[seq] === 'undefined') {
            results[seq] = [current, 0];
        }

        results[seq][1]++;

        if (current !== next) {
            seq++;
        }

        i++;
    }

    return results;
}

var actual = letterCount('eeeeeoooohhoooee');
var expected = [["e", 5],["o",4],["h",2],["o",3],["e",2]];

assert.deepEqual(actual, expected);

1
我会使用以字符为键的映射来存储连续字符的计数,然后在最后构建输出结构。根据您的示例,我不确定您对“连续”有什么确切的理解,但您可以调整逻辑以相应地识别连续数字。
function LetterCount(str) {
  var counts = {};
  for (var i = 0, prevChar = null; i < str.length; i++) {
    var char = str.charAt(i);
    if(counts.hasOwnProperty(char) && char === prevChar) {
      counts[char] = counts[char] + 1;  
    } else if (!counts.hasOwnProperty(char)) {
      counts[char] = 0;
    }
    prevChar = char;
  }
  var res = [];
  for (var char in counts) {
    if (counts.hasOwnProperty(char)) {
      res.push([char,counts[char]);
    }
  }
  return res;
}

0

const string = 'acbbaeekzzkeee';

function letterCount(str) {
  return [...str].reduce((acc, letter, index) => {
    if (index === 0 || letter !== str[index - 1]) {
      acc.push([letter, 1]);
    } else {
      acc[acc.length - 1][1]++;
    }
    return acc;
  }, []);
}

const count = letterCount(string);
console.log(count);

  1. 将字符串分散成数组
  2. 使用初始值为空数组的reduce循环遍历它
  3. 如果它是第一个字母或前一个字母与当前字母不同
  4. 创建一个包含[字母,1]的数组,并将其推入累加器数组中(1是初始计数/值)
  5. 否则,在累加器数组中递增先前数组的值。由于先前的数组将具有与当前字母相同的字母
  • 因此,每当我们有新值(非连续值)时,我们将一个数组添加到数组中。
  • 否则,我们会递增最后一个数组的值(它将是相同的字母)

0
function LetterCount(text){
    arr = [];
    letter = text[0];
    counter = 0;
    for (c in text+' '){
        if (text[c] != letter){
            newArr = [letter, counter];
            arr.push(newArr);
            letter = text[c];
            counter = 0;
        }
        counter += 1;
    };
    return arr;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接