JavaScript:使用对象迭代数组并跟踪项目频率

时间:2016-09-16 00:42:24

标签: javascript arrays object javascript-objects

我需要一个函数mostFrequentWord来返回数组words中找到的最常见的字符串。我想用一个对象来跟踪这些单词频率。使用getter和setter方法似乎是最可行的选择。其中setter函数用于更改表示单词的每个键的值。然后,在按频率值对对象进行排序后,我可以返回频率最高的单词。我在想这个问题吗?

5 个答案:

答案 0 :(得分:1)

以下是使用Array.prototype.reduce()

解决此问题的方法



var words = ["one", "three", "three", "three", "two", "two"];

var frequencies = words.reduce(function(memo, word) {
    //either start the count now, if this is the first encounter or increas it by 1
    memo[word] = (memo[word] + 1) || 1;
    return memo;
}, {}); // note the empty object being passed in here - that's the initial value for the variable "memo"

console.log(frequencies);

var mostFrequentWord = Object.keys(frequencies)
  .reduce(function(highest, current) {
    return frequencies[highest] > frequencies[current] ? highest : current;
  }, "");

console.log("most frequent word: " + mostFrequentWord + 
"\ncount: " + frequencies[mostFrequentWord])




要获得最高价值,它就像再次运行reduce一样简单,只有这次使用Object.keys()

编辑:发表评论:

Is there any advantage to using .reduce() over .forEach() in your first loop? You're just returning the same object every time so it seems that .forEach() would work just as well and perhaps be a little clearer

嗯,它有点落后于风格 - 这些都可以达到相同的效果。他们这样做的方式虽然不同,但我认为,reduce至少具有微不足道的优势。原因如下:

  1. reduceforEach表达了不同的意图。虽然它们都可以用来实现类似的结果,但它们的运行方式的差异确实使它们对某些操作有点偏颇。

    • 对于reduce,意图是"我想要收集这些东西,经过它并返回一件东西"。例如,它非常适合于找到最小值或最大值或总和。所以,如果你在开始时有一个数组并且希望以其他东西结束,你会使用它(尽管有时,你也可以返回一个数组)。
    • forEach的意图虽然略有不同,但它是"我想通过这个系列并对每个项目做些什么"。从本质上讲,当你想对每个对象进行相同的操作时,例如,你可能console.log ging它们,或验证它们或上传它们。一般情况下,您将拥有一个代码,其中包含一个项目并使用它执行某些操作,您只需通过forEach将其应用于所有项目。
  2. reduce是自包含的。它看起来可能不是很多,并且它可能不会取决于上下文,但您必须认识到整个功能都包含在reduce中。这使得在更大的环境中更容易掌握,因为您在一个地方拥有所需的一切。让我们使用forEach重新编写它,我会尝试显示差异

  3. 
    
    var words = ["one", "three", "three", "three", "two", "two"];
    
    var frequencies = {}; //<- instantiation needs to be separate
    
    words.forEach(function(word) { //<- population needs to be separate
        frequencies[word] = (frequencies[word] + 1) || 1;
    });
    
    console.log(frequencies); //<- usage is separate
    &#13;
    &#13;
    &#13;

    因此,您将函数缩短一行(不返回),但由于变量的实例化而获得一行。这看起来完全正常,因为它是孤立的,但在更大的代码库中,您可能在每个部分之间都有代码。这使得保持所有逻辑变得更加困难 - 如果你只读 forEach循环,那么你就没有完整的背景,因为你需要知道frequencies滚动到它时,您可能无法看到forEach。更重要的是,你甚至不知道状态frequencies会在什么状态下进入forEach - 是否会预先填充一些值?它会被设置为null吗?它会是一个数组而不是一个对象吗?您不仅需要找到frequencies的初始声明,而且还必须在调用函数之前的任何时刻跟踪,如果它已被更改。

    现在,有了这个说法,让我们重新审视一下reduce做什么 - 所有你需要了解它的运作方式。 frequencies的声明,所有更改和最终赋值总是发生在三行代码的范围内,因此无论您拥有多少代码,您都不需要为上下文找到任何其他内容。是的,您可能需要知道words包含的内容,但是forEach也是如此。

    关于这两点,我想说reduce更清楚。 forEach似乎更简单的解决方案的唯一原因是,如果您只使用常规for循环执行操作,并且需要功能替换。然而,声明性方法与命令性有所不同 - forEachfor是不同的。这两者本身并不是更好,但根据具体情况,它们确实有优点和缺点。在这种情况下,reduce操作是更好的功能方法。

答案 1 :(得分:0)

它是这样的:

function inArrayToIndex(value, array){
  for(var i=0,l=array.length; i<l; i++){
    if(array[i] === value){
      return i;
    }
  }
  return false;
}
function mostFrequentWord(wordsArray){
  var h = [], w, a, c = [], m;
  for(var i=0,l=wordsArray.length; i<l; i++){
    w = wordsArray[i]; a = inArrayToIndex(w, h)
    if(a !== false){
      c[a]++;
    }
    else{
      h.push(w); c.push(1);
    }
  }
  return h[inArrayToIndex(Math.max.apply(null, c), c)];
}
var mostest = mostFrequentWord(yourWordsArray);

答案 2 :(得分:0)

感谢所有输入的家伙和女孩们。以下是我如何解决它。

首先我开始使用辅助函数:

function getTokens(rawString) {
  // returns an alphabetically sorted list of words, removing punctuation
  // characters
  return rawString.toLowerCase().split(/[ ,!.";:-]+/).filter(Boolean).sort();
}

然后我的主要功能如下:

function mostFrequentWord(words) { 
  var wordsArray = getTokens(words);           // setup array for strings to live
  var wordsObject = {};                        // Setup object literal for words + count

  for (var i=0; i<wordsArray.length; i++) {
    var wordToCheck = wordsArray[i];
    if (wordsObject[wordToCheck] == undefined) {
      // word doesn't exist, let's add it as a key and set value pair to 1
      console.log(wordToCheck + " not found. Adding to object.");
      wordsObject[wordToCheck] = 1;
    } else {
      // word does exist, let's increment the value pair by 1
      console.log(wordToCheck + " has been found. Incrementing.");
      wordsObject[wordToCheck] += 1;
    }
  }

console.log(wordsObject);
var mostFrequent;

  for (var key in wordsObject) {
    if (mostFrequent == undefined) {
      mostFrequent = key;
    } else if (wordsObject[key] > wordsObject[mostFrequent]) {
       mostFrequent = key;
    }
  }

console.log("Most frequent word is: " + mostFrequent);
return mostFrequent;

}

答案 3 :(得分:-1)

您可以使用对象来保持单词索引计数,然后遍历计数以获得最高计数。这是一个工作片段,说明了:

&#13;
&#13;
function findMostFrequent(array) {
    var map = {};
    
    array.forEach(function(item) {
        map[item] = (map[item] || 0) + 1;
    });

    // find highest word count
    var highWord = Object.keys(map).reduce(function(highestWord, currentWord) {
        return map[currentWord] > map[highestWord] ? currentWord : highestWord;
    });
    return {word: highWord, count: map[highWord]};
}

var words = ["hello", "goodbye", "hello", "hello", "whatever", "something", "goodbye"];

var result = findMostFrequent(words);
console.log("highest count word is " + result.word + ", count = " + result.count);
&#13;
&#13;
&#13;

在ES6中,您可以使用Map对象来保留计数而不是普通的JS对象,尽管在实现方式上差别不大。

答案 4 :(得分:-1)

这是另一个使用lodash

的解决方案

var words = ["bob", "bill", "jimmy", "jack", "bob", "bob", "jimmy"];
    freq = {};

_.forEach(words, function (word) {
  freq[word] = freq[word]++ || 1;
});

var max = 0,
    mostFreq = undefined;

_.forEach(freq, function (count, word) {
  if (count > max) {
    max = count;
    mostFreq = word;
  }
});

console.log(mostFreq);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

for each函数在javascript中是原生的,但仅适用于数组。使用lodash,您可以遍历数组的每个元素,或遍历对象的每个键值对。将_.forEach与对象一起使用时,传入回调的第一个参数是值,第二个参数是对象中每对的键。你应该看看lodash文档。它们提供了一些非常有用的工具。