JavaScript:使用对象迭代数组并跟踪条目频率[关闭]

wkftcu5l  于 2023-03-28  发布在  Java
关注(0)|答案(5)|浏览(116)

已关闭。此问题为opinion-based。当前不接受答案。
**想要改进此问题吗?**请更新此问题,以便editing this post可以用事实和引文来回答。

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

sg24os4d

sg24os4d1#

下面是如何使用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()
回应评论:
在第一次循环中使用.reduce()比使用.forEach()有什么好处吗?每次都返回相同的对象,所以看起来.forEach()同样有效,而且可能更清晰一些
好吧,这在某种程度上取决于风格-这两个 * 可以 * 实现相同的结果。虽然它们实现的方式不同,但我认为,由于这个原因,reduce至少有一个微小的优势。原因如下:

  1. reduceforEach传达了不同的意图。虽然它们都可以用于实现类似的结果,但它们操作方式的差异确实使它们对某些操作有点偏见。
  • 对于reduce,它的意图是“我想获取这个集合,遍历它并返回一个东西”。它非常适合于查找最小值或最大值,或者求和。因此,如果您在开始时有一个数组,并且希望以其他内容结束,则可以使用它(尽管有时,您也可以返回数组)。
  • forEach的意图略有不同-它是“我想通过这个集合并对每个项目做一些事情”。本质上,它是当你想对每个对象做相同的操作时,比如说,你可能是console.log ging它们,或者验证它们或者上传它们。一般来说,你会有一个一些代码,它接受一个项目,并对它做一些事情,你只需要通过forEach将它应用到所有项目。
  1. 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的作用--你需要知道的关于reduce如何操作的所有信息都在一个地方。frequencies的声明、所有更改和最终赋值总是发生在三行代码中,所以不管你有多少代码,你都不需要为上下文找到任何其他东西。是的,您可能需要知道words包含什么,但是,对于forEach也是如此。
关于这两点,我想说reduce更容易理解。forEach看起来更简单的唯一原因是如果你只使用常规的for循环来做事情,你需要一个功能性的替代品。然而,声明式方法与命令式方法有其不同之处-forEachfor是不同的。2两者都不是天生的 * 更好 *,但它们确实根据情况有优点和缺点。在这种情况下,reduce操作是更好的函数方法。

6jygbczu

6jygbczu2#

事情是这样的:

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

neekobn83#

感谢所有的输入的家伙和女孩。这是我如何去解决它。
首先,我使用一个helper函数:

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;

}
js4nwp54

js4nwp544#

下面是另一个解决方案,它使用了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文档。他们提供了一些非常有用的工具。

rkue9o1l

rkue9o1l5#

你可以使用一个对象来保持一个单词的索引计数,然后迭代计数以获得最高的一个。下面是一个工作片段,它说明了:

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对象,尽管两者在实现上几乎没有区别。

相关问题