2

我有一个数组,其中包含已发生的事件,它们是用自由文本编写的,因此除了某些关键字之外没有遵循某种模式,例如。“抢劫”、“凶手”、“入室行窃”、“车祸”等。这些关键字可以在文本中的任何位置,我想找到这些关键字并将它们添加到类别中,例如。“抢劫”。

最后,当我检查完所有事件后,我想要一个这样的类别列表:

Robberies: 14
Murder attempts: 2
Car accidents: 5
...

数组元素可能如下所示:

incidents[0] = "There was a robbery on Amest Ave last night...";
incidents[1] = "There has been a report of a murder attempt...";
incidents[2] = "Last night there was a housebreaking in...";
...

我想这里最好的是使用正则表达式来查找文本中的关键字,但我真的很讨厌正则表达式,因此在这里需要一些帮助。

下面的正则表达式不正确,但我猜这个结构会起作用吗?有没有更好的方法来避免 DRY?

var trafficAccidents = 0,
    robberies = 0,
    ...

function FindIncident(incident) {
    if (incident.match(/car accident/g)) {
        trafficAccidents += 1;
    }
    else if (incident.match(/robbery/g)) {
        robberies += 1;
    }
    ...
}

提前非常感谢!

4

7 回答 7

2

实际上,我在这里有点不同意你的看法。. . 我认为像这样的字符串函数indexOf会很好地工作。

我会使用 JavaScript 的 indexOf 方法,它需要 2 个输入:

string.indexOf(value,startPos);

所以你可以做的一件事就是定义一个简单的临时变量作为你的游标。. .

function FindIncident(phrase, word) {
    var cursor = 0;
    var wordCount = 0;
    while(phrase.indexOf(word,cursor) > -1){
        cursor = incident.indexOf(word,cursor);
        ++wordCount;        
    }
    return wordCount;
}

我没有测试过代码,但希望你能明白。. .

如果您确实使用它,请特别注意起始位置。

于 2013-01-09T23:50:20.410 回答
2

以下代码显示了您可以采用的方法。你可以在这里测试

var INCIDENT_MATCHES = {
  trafficAccidents: /(traffic|car) accident(?:s){0,1}/ig,
  robberies: /robbery|robberies/ig,
  murder: /murder(?:s){0,1}/ig
};

function FindIncidents(incidentReports) {
  var incidentCounts = {};
  var incidentTypes = Object.keys(INCIDENT_MATCHES);
  incidentReports.forEach(function(incident) {
    incidentTypes.forEach(function(type) {
      if(typeof incidentCounts[type] === 'undefined') {
        incidentCounts[type] = 0;
      }
      var matchFound = incident.match(INCIDENT_MATCHES[type]);
      if(matchFound){
          incidentCounts[type] += matchFound.length;
      };
    });
  });

  return incidentCounts;
}

正则表达式是有意义的,因为您将拥有许多符合您的“匹配”标准的字符串,即使您只考虑“抢劫”的复数和单数形式的差异。您还希望确保您的匹配不区分大小写。

您需要在正则表达式上使用“全局”修饰符,以便匹配诸如“谋杀、谋杀、谋杀”之类的字符串,并将计数增加 3 而不仅仅是 1。

这使您可以将匹配标准和事件计数器之间的关系保持在一起。它还避免了对全局计数器的需要(grantedINCIDENT_MATCHES在这里是一个全局变量,但您可以很容易地将它放在其他地方并将其移出全局范围。

于 2013-01-09T23:56:25.687 回答
1

你可以做这样的事情,它将抓取在数组中每个项目上找到的所有单词,它会返回一个带有计数的对象:

var words = ['robbery', 'murderer', 'housebreaking', 'car accident'];

function getAllIncidents( incidents ) {
  var re = new RegExp('('+ words.join('|') +')', 'i')
    , result = {};
  incidents.forEach(function( txt ) {
    var match = ( re.exec( txt ) || [,0] )[1];
    match && (result[ match ] = ++result[ match ] || 1);
  });
  return result;
}

console.log( getAllIncidents( incidents ) );
//^= { housebreaking: 1, car accident: 2, robbery: 1, murderer: 2 }

这更像是一个快速原型,但可以通过复数和多个关键字进行改进。

演示:http: //jsbin.com/idesoc/1/edit

于 2013-01-10T00:05:06.440 回答
1

使用对象数组来存储您正在搜索的所有许多不同类别,并带有适当的正则表达式和计数成员,您可以将整个内容写成四行。

var categories = [
    {
        regexp: /\brobbery\b/i
        , display: "Robberies"
        , count: 0
    }
    , {
        regexp: /\bcar accidents?\b/i
        , display: "Car Accidents"
        , count: 0
    }
    , {
        regexp: /\bmurder\b/i
        , display: "Murders"
        , count: 0
    }
];

var incidents = [ 
    "There was a robbery on Amest Ave last night..."
    , "There has been a report of an murder attempt..."
    , "Last night there was a housebreaking in..."
];

for(var x = 0; x<incidents.length; x++)
    for(var y = 0; y<categories.length; y++)
        if (incidents[x].match(categories[y].regexp))
            categories[y].count++;

现在,无论您需要什么,您只需编辑一段代码,它就会在您的代码中传播。

此代码有可能将每个事件分类为多个类别。为了防止这种情况,只需在 if 块中添加一个“break”语句。

于 2013-01-09T23:58:29.897 回答
1

使用对象来存储您的数据。

events = [
    { exp : /\brobbery|robberies\b/i, 
    //       \b                      word boundary
    //         robbery               singular
    //                |              or
    //                 robberies     plural
    //                          \b   word boundary
    //                            /i case insensitive
      name : "robbery",
      count: 0
    },
    // other objects here
]

var i = events.length;    
while( i-- ) {

    var j = incidents.length; 
    while( j-- ) {

        // only checks a particular event exists in incident rather than no. of occurrences
        if( events[i].exp.test( incidents[j] ) { 
            events[i].count++;
        }
    }
}
于 2013-01-09T23:49:03.437 回答
1

RegEx 也让我头疼。;) 如果您正在寻找完全匹配并且不担心拼写错误和拼写错误,我会在事件字符串中搜索包含您要查找的关键字的子字符串。

incident = incident.toLowerCase();
if incident.search("car accident") > 0 {
    trafficAccidents += 1;
}
else if incident.search("robbery") > 0 {
    robberies += 1;
}
...
于 2013-01-09T23:54:13.717 回答
1

是的,这是一种方法,虽然用正则表达式匹配纯单词有点矫枉过正——在这种情况下,你应该使用indexOf作为 rbtLong 建议的。

您可以通过以下方式进一步完善它:

  • 附加i标志(匹配小写和大写字符)。
  • 为您的表达添加可能的单词变体。robbery可以翻译成robber(y|ies),从而匹配单词的单数和复数变体。car accident可能是(car|truck|vehicle|traffic) accident

单词边界 \b

不要使用这个。它需要在您的匹配词周围使用非字母数字字符,并防止匹配错别字。你应该让你的查询尽可能的清晰。


if (incident.match(/(car|truck|vehicle|traffic) accident/i)) {
    trafficAccidents += 1;
}
else if (incident.match(/robber(y|ies)/i)) {
    robberies += 1;
}

注意我是如何丢弃g旗帜的;它代表“全局匹配”并使解析器在第一次匹配后继续搜索字符串。这似乎没有必要,因为只需一次确认的事件就足以满足您的需求。

这个网站提供了一个很好的正则表达式介绍

http://www.regular-expressions.info/tutorial.html

于 2013-01-10T00:21:46.607 回答