缩短Javascript函数

11

我写了一个函数,可以将一个字符串转换成缩写,但目前它比较长并且区分大小写。

我需要一种方法来缩短它,以便它在任何情况下都能正常工作。目前,如果分隔单词中有大写字母或单词以分隔单词结尾,它就会出现问题。

我的分隔单词基本上是我要删除的单词(因为大多数公司等并不包括它们)。 它们包括:

  • and
  • of
  • the
  • for
  • to

另外,我删除它们的方法是使用split和join(str.split('and ').join('')),但对我来说似乎不是最简单的方法。

除了这些问题之外,它工作得很好。 有人能帮我缩小函数并解决这些问题吗? 谢谢。

Function:

String.prototype.toAbbrev = function () {
    var s = [];
    var a = this.split('and ').join('').split('of ').join('').split('the').join('').split('for ').join('').split('to ').join('').split(' ');
    for (var i = 1; i < a.length + 1; i++) {
        s.push(a[i - 1].charAt(0).toUpperCase());
    }

    return s.join('.');
}

已测试公司输出

The National Aeronautics and Space Administration           ->    N.A.S.A
The National Roads and Motorists' Association               ->    N.R.M.A
Royal Society for the Prevention of Cruelty to Animals      ->    R.S.P.C.A

5
使用一个正则表达式替换怎么样?你可以使用 i 修饰符让它不区分大小写。 - Barmar
不要为此扩展字符串原型。在JavaScript中,扩展本地原型通常是非常不受欢迎的。只需使用常规函数-没有任何问题。 - George Mauer
@GeorgeMauer 我知道这是不被赞同的。只是我从来没有理解为什么,而且它从未对我产生任何影响,所以我没有理由不这样做。 - Spedwards
此时值得这样做,只是因为它是如此强烈的约定,如果人们看到这个,他们会感到非常愤怒。规则的原因是该技术增加了可能的故障点,而且除了略微不同的语法之外没有任何好处。首先,它需要使用 this 参数,大多数人并不真正理解其规则或(通常)更简单的替代方法。其次,你当然可以覆盖其他函数。最后,它具有与全局函数相同的所有限制,其命名空间为字符串并不真正改变作用域机制。 - George Mauer
最大的问题是如果有人依赖于你的代码,这会在他们的对象上创建方法。比如说,他们之前在“String”中迭代属性,现在开始得到不同的值!更符合惯用法的方法是使用模块加载器,如require、almond或browserfy,并简单地编写函数。这很有道理——函数是JavaScript中唯一限制作用域的东西,其他所有东西都是为了装饰而加入的,其中许多是为了营销而添加的,以使语言类似于Java。 - George Mauer
显示剩余4条评论
6个回答

12

我认为这样的方法可能会更有效:

var toAbbrev = function(str){
    return str.replace(/\b(?:and|of|the|for|to)(?: |$)/gi,''). // remove all occurances of ignored words
               split(' ').                                     // split into words by spaces
               map(function(x){                          
                   return x.charAt(0).toUpperCase();           // change each word into its first letter capitalized
               }).
               join('.');                                      // join with periods
};

以下是正则表达式的详细解析:

/
    \b                    // word boundary
    (?:and|of|the|for|to) // non-capturing group. matches and/of/the/for/to
    (?: |$)               // non-capturing group. matches space or end of string
/gi                       // flags: g = global (match all), i = case-insensitive

这里有一种替代方法,其正则表达式更简单:

var toAbbrev = function(str){
    return str.split(' '). // split into words
               filter(function(x){
                   return !/^(?:and|of|the|for|to)$/i.test(x); // filter out excluded words
               }).
               map(function(x){
                    return x.charAt(0).toUpperCase(); // convert to first letter, captialized
               }).
               join('.'); // join with periods
};

正则表达式的分解:

/
    ^                     // start of string
    (?:and|of|the|for|to) // non-capturing group. matches and/of/the/for/to
    $                     // end of string
/i                        // flags: i = case-insensitive

为什么不直接使用.replace(/(.).+?(\s|$)/g, "$1")呢? - Derek 朕會功夫
@Derek朕会功夫 这也是一种可接受的方法,但它不会大写。 - nderscore
1
是的,但你总是可以将结果和结尾大写 ;) - Derek 朕會功夫
我认为这种方法比使用纯正则表达式要好得多,但在某些情况下仍需要将其添加到排除列表中 http://jsfiddle.net/Xotic750/RX37n/ 我还会选择使用 x.charAt(0) 而不是 x[0],因为这样你可以轻松地插入适当的ECMA5 pollyfils,而且你就不会遇到不能处理直接字符串索引的环境(最明显的是IE) - Xotic750
@Xotic750 我更新了我的解决方案,使用了 charAt。我尽量不去想IE :P - nderscore

8
一个更短的例子:
str.replace(/(and|of|the|for|to)( |$)/gi, "").replace(/(.).+?(\s|$)/g, "$1.");

为确保字母大写,您可以在末尾添加 .toUpperCase
(.)     //selects the first character
.+      //matches the rest of the characters
  ?     //? indicates a lazy match
(\s|$)  //match a space or the end

$1.     //means "the first selected match plus a dot"

让我们把它合并为一个正则表达式!
str.replace(/((and|of|the|for|to) )*(.).+?(\s|$)/ig, "$3.");

"Royal Society for the Prevention of Cruelty to Animals"
    .replace(/((and|of|the|for|to) )*(.).+?(\s|$)/ig, "$3.");
//R.S.P.C.A

"Josie and the Pussycats"
    .replace(/((and|of|the|for|to) )*(.).+?(\s|$)/ig, "$3.");
//J.P.

理论上,这应该涵盖所有合法的名称。对于以介词结尾的名称,你在技术上可以这样做:

.replace(/((and|of|the|for|to) )*(.).+?(\s|$)((and|of|the|for|to) ?)*/ig, "$3.")

但这个明显比使用两个replace要长,这违背了它的初衷。


Derek,请解释一下后半部分的正则表达式:/(.).+?(\s|$)/。谢谢。 - james emanon
@nderscore - 现在可以了 - Derek 朕會功夫
1
这个解决方案对某些字符串无效。例如:"Department of Homeland Security" => "D.H." - nderscore
我假设你的答案是最终的 replace?http://jsfiddle.net/Xotic750/XZkpC/ - Xotic750
我撤回之前的评论。单个替换可以通过第二个参数中的函数实现:Pstr.replace(/[^ ]+ ?/g, function(x){ return /^(and|of|the|for|to) ?$/i.test(x)?'':x[0]+'.'; }); - nderscore
显示剩余13条评论

4

您也可以使用reduce完成此操作。实质上,您正在将字符串缩减为缩写 -

str.split(' ').reduce(function(preV, curV, index) {
    if(!/^(and|of|the|for|to)$/.test(curV.toLowerCase())) {
        return preV + curV.toUpperCase().charAt(0) + '.';
    }
    return preV;
}, '');

“reduce” 似乎是一个不错的选择,但您可以通过使用 ECMA5 的 “indexOf” 而不是正则表达式,并使用排除列表来改进它。并且反转 toUpperCase().charAt(0) 将会减少一些工作量。 - Xotic750
1
如果您想加速,可以将“str.match(rx)”替换为“rx.test(str)”,因为您不需要捕获... - dandavis
现在你在某些情况下遇到了问题 TypeError: Object the has no method 'test'。http://jsfiddle.net/Xotic750/4C4jX/ - Xotic750
是的,我也怀疑过,但懒得检查兼容性。现在已经改回来匹配了。 - Mukesh Soni
1
@Xotic750的意思是test是RegExp的一个方法,不是字符串的。应该写成!/and|of|the|for|to/.test(curV.toLowerCase())。当正确使用它时,你永远不应该收到那个错误信息。 - nderscore
显示剩余4条评论

2
为什么不尝试这样做呢?
var a=this.replace(/and |of |the |for |to /gi, '').split(' ');

否则,其他部分看起来都很好。

2
只需按以下方式进行字符串替换:
var a = this.replace(/ and | of | the | for | to /gi, ' ').split(' ');

这也将解决一个问题,即分割词之一在任何主要单词的末尾。要删除字符串开头的任何分割词,请按以下步骤操作:
var pos = a.search(/and |of |the |for |to /i);
if (pos == 0)
   //remove that word

2
使用ECMA5的可能解决方案
Javascript
var toAbbrev = (function (ignore) {
    return function toAbbrev(myString) {
        return myString.split(/[^\w]/).reduce(function (acc, word) {
            if (word && ignore.indexOf(word.toLowerCase()) === -1) {
                acc += word.charAt(0).toUpperCase() + '.';
            }

            return acc;
        }, '');
    };
}(['and', 'of', 'the', 'for', 'to']));

console.log(toAbbrev('The Silica & Sand Society'));
console.log(toAbbrev('The National Aeronautics and Space Administration'));
console.log(toAbbrev('The National Roads and Motorists\' Association'));
console.log(toAbbrev('Royal Society for the Prevention of Cruelty to Animals'));

输出

S.S.S.
N.A.S.A.
N.R.M.A.
R.S.P.C.A. 

jsFiddle上, 你可能可以改进split的正则表达式(/[^\w]/)来处理更多的奇怪情况。或者只在空白字符上拆分/\s/并添加到排除列表中。

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