【问题标题】:JavaScript: Using an object to iterate over array and keep track of item frequency [closed]JavaScript:使用对象迭代数组并跟踪项目频率[关闭]
【发布时间】:2016-09-16 00:42:24
【问题描述】:

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

【问题讨论】:

  • 是的,你想多了。
  • 好的,我想我解决了。是的,vlaz,你是对的....想多了。
  • @BryanSwagerty - 你收到了一堆答案,告诉你如何做到这一点。由于看起来您可能是新来的,因此堆栈溢出的适当做法是从您收到的答案中选择最佳答案,然后单击答案左侧的绿色复选标记。如果您认为您自己的答案是原创的并且比此处提供的任何答案都好,请发布您自己的答案并选择它。但是,如果问题已经解决,你不应该把问题留在这里。通过选择带有复选标记的最佳答案,向社区表明它已得到解决。
  • 感谢@jfriend00,截至今天早上,我已经接受了似乎最好地解决它的答案。同时,我还发布了编码的解决方案。谢谢大家。

标签: javascript arrays object javascript-objects


【解决方案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.logging 它们,或者验证它们或上传它们。通常,您将拥有一些代码,该代码需要一个项目并用它做某事,您只需通过forEach 将其应用于所有项目。
  2. reduce 是独立的。它可能看起来不多,并且根据上下文可能不多,但您必须认识到整个功能都包含在reduce 中。这使得在更大的范围内更容易掌握,因为你在一个地方拥有你需要的一切。让我们用forEach 重写它,我会尝试显示不同之处

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

因此,您将函数缩短了一行(不返回),但由于变量的实例化而增加了一行。现在看起来完全没问题,因为它是孤立的,但在更大的代码库中,每个部分之间可能都有代码。这使得将所有逻辑都牢记在脑海中变得更加困难 - 如果您阅读 forEach 循环,则您没有完整的上下文,因为您需要了解 frequencies 何时你滚动到它,你可能看不到forEach。更重要的是,您甚至不知道frequencies 会处于什么状态,然后您会到达forEach - 它会预先填充一些值吗?它会设置为null吗?它会是一个数组而不是一个对象吗?您不仅需要找到frequencies 的初始声明,而且还必须跟踪如果它在函数被调用之前的任何时间点被更改。

现在,话虽如此,让我们重新检查一下 reduce 的作用 - 所有您需要了解的关于它如何运作的一切都在一个地方。 frequencies 的声明、所有更改和最终分配总是发生在三行代码的范围内,因此无论您有多少代码,您都不需要为上下文找到任何其他内容。是的,您可能需要知道words 包含什么,但是forEach 也是如此。

关于这两点,我想说reduce 更容易理解。 forEach 看起来像更简单的解决方案的唯一原因是,如果您只使用常规的 for 循环来做事情,并且您需要功能替换。然而,声明式方法与命令式方法不同 - forEachfor 是不同的。两者都不是天生的更好,但根据情况,它们确实有优点和缺点。在这种情况下,reduce 操作是更好的功能方法。

【讨论】:

  • 在你的第一个循环中使用.reduce().forEach() 有什么优势吗?您只是每次都返回相同的对象,因此.forEach() 似乎也可以正常工作,并且可能会更清晰一些。
  • 我在帖子中添加了一些内容,说明我为什么选择reduce。 TL;DR - 是的,您可以使用其中任何一种,但 reduce 有其优势,而 forEach 只是“完成工作”,本身并没有真正提供太多。
  • 根据你自己的描述,.forEach() 是“我想浏览这个集合并对每个项目做点什么”。这正是第一个循环正在做的事情。它将每个项目添加到频率对象。不多也不少。它没有找到最大值或最小值,也没有返回一个减少的结果。可以很容易地使用一个常见的for 循环。在第一个循环中,我仍然看不到 .reduce() 的微小优势,而且 .reduce() 似乎不太直接。第二个循环确实适合.reduce()
  • That is exactly what the first loop is doing. 不,不完全是。 forEach 旨在每次完全做同样的事情。理想情况下,它需要一个纯函数 - 修改外部状态(frequency 变量是外部的)将使函数不纯。这使得函数 not 每次都表现相同。 reduce,但是是完全可以预测的,因为它(有效地)将frequencies 作为输入,这是完全纯粹的。这看起来像是学术上的差异,但它 forEach 和其余函数方法背后的意图。
  • 好的,我们可以同意不同意。您现在正在进行学术论证以试图捍卫您的观点。我正在实用且轻松地查看代码的作用。你已经表达了为什么你更喜欢.reduce()。我不同意。两种不同的意见。无需进一步讨论。
【解决方案2】:

是这样的:

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);

【讨论】:

  • 我认为这行不通。根据一个简单的输入运行它,它返回false
  • 是的。 mostFrequentWord(["one", "two", "one"]) // false
  • 现在是缩短版。
  • 我在inArrayToIndex 函数中看不到重点。 [6, 4, 12].indexOf(4) 已经与该函数执行相同的操作(除了返回 -1 而不是 false),只有 1. 更容易 2. 更快 3. 它是内置的
  • 如果我必须支持 IE8 或更低版本(而且我已经在工作中做...),我会已经对这个方法进行了 polyfill,或者会使用库来进行集合操作。我认为完全有理由假设在这个时代人们要么不支持 IE8,要么他们知道他们可能需要以某种方式模拟某些功能。
【解决方案3】:

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

首先我从一个辅助函数开始:

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;

}

【讨论】:

    【解决方案4】:

    这是另一个解决方案,它使用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);
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"&gt;&lt;/script&gt;

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

    【讨论】:

    • 如果您打算使用 lodash,请不要仅将其用作 JS 中已有功能的替代品。这是一种浪费。 _.countBy(words) 将在一行代码中为您提供计数。
    【解决方案5】:

    您可以使用一个对象来保存一个单词索引计数,然后遍历计数以获得最高的计数。这是一个有效的 sn-p 说明:

    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);

    在 ES6 中,您可以使用 Map 对象而不是普通的 JS 对象来保存计数,尽管这两种方式的实现几乎没有区别。

    【讨论】:

    • 为什么投反对票?这是工作代码。运行 sn -p 看看。
    • 我觉得人们只是痴迷于自己的答案。我认为 SO 的主要目的是提供答案,有人反对我提供提供确切答案的外部链接。我本可以厚颜无耻地把它复制到这里,但我想 SO 是一个战场
    • @SenthilNatarajan - 这里有关于答案的规则,这些规则是有目的的。此处不允许将全部内容包含在外部链接中的答案,因为外部链接会随着时间的推移而改变或消失,使得答案作为持久的知识库毫无价值,而这是堆栈溢出的目标之一。因此,您发布的内容适合发表评论,而不是作为答案。如果您想提供符合此处规则的答案,您可以从该文章中提取内容并将其复制到答案中。
    猜你喜欢
    • 2017-07-07
    • 1970-01-01
    • 2016-08-06
    • 2017-03-29
    • 2019-02-27
    • 2019-07-27
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    相关资源
    最近更新 更多