【问题标题】:Efficient way to get top count of an option in an array of objects获取对象数组中选项的最高计数的有效方法
【发布时间】:2021-07-30 11:07:16
【问题描述】:

我有一个这样的对象数组,

[
  {
    user: 'A',
    answers: [
      {
        id: 1,
        score: 3,
      },
      {
        id: 2,
        score: 1,
      },
      ...
    ]
  },
  {
    user: 'B',
    answers: [
    ...

我有 200 个用户,每个用户回答一组 40 个问题,每个问题都有一个 id 和一个分数。

我要做的是将每个问题的分数相加。这样我就可以找出哪个问题得分最高,哪个问题得分最低。也就是顶部问题和底部问题。

最好的方法是什么?

我目前的做法感觉有点啰嗦。

const allAns = []

myList.forEach( user => allAns.push( ...user.answers ) )

const questionsScored = allAns.reduce( ( obj, cur ) => {
  !obj[ cur.id ] ? obj[ cur.id ] = cur.score : obj[ cur.id ] += cur.score
  return obj
}, {} )

const sortingList = []
for ( const qn in questionsScored ) {
  sortingList.push( [ qn, questionsScored[ qn ] ] )
}
sortingList.sort( ( a, b ) => b[ 1 ] - a[ 1 ] )

console.log( sortingList[ 0 ], sortingList[ sortingList.length - 1 ] )

【问题讨论】:

  • 你有没有尝试过?在这里发布吗?
  • 更新了我目前正在使用的帖子。

标签: javascript arrays loops object ecmascript-6


【解决方案1】:

您正在采取所有必要的步骤,所以如果它正常工作,那很好,尽管您可以用可用的方法替换一些 forEach() 循环:

.flatMap()

const allAns = myList.flatMap(({answers})=>answers);

并使用Object.entries()

const sortingList = Object.entries(questionsScored);

const
  input = [{ user: 'A', answers: [{ id: 1, score: 3, }, { id: 2, score: 1, }, { id: 3, score: 0, }] }, { user: 'B', answers: [{ id: 1, score: 2, }, { id: 2, score: 1, }, { id: 3, score: 0, }] },],

  allAns = input.flatMap(({ answers }) => answers),

  questionsScored = allAns.reduce((obj, cur) => {
    !obj[cur.id] ? obj[cur.id] = cur.score : obj[cur.id] += cur.score
    return obj
  }, {}),

  sortingList = Object.entries(questionsScored).sort((a, b) => b[1] - a[1]);

console.log({ max: sortingList[0], min: sortingList[sortingList.length - 1] })

或者合并成一个单独的链式调用,但不一定更好。

const
  input = [{ user: 'A', answers: [{ id: 1, score: 3, }, { id: 2, score: 1, }, { id: 3, score: 0, }] }, { user: 'B', answers: [{ id: 1, score: 2, }, { id: 2, score: 1, }, { id: 3, score: 0, }] },],

  sortingList = Object
    .entries(
      input
        .flatMap(({ answers }) => answers)
        .reduce((obj, cur) => {
          !obj[cur.id] ? obj[cur.id] = cur.score : obj[cur.id] += cur.score
          return obj
        }, {})
    )
    .sort((a, b) => b[1] - a[1]);

console.log({ max: sortingList[0], min: sortingList[sortingList.length - 1] })

如果您想避免sort() 调用,您可以在初始reduce() 之后使用forEach() 来收集低计数和高计数

const
  input = [{ user: 'A', answers: [{ id: 1, score: 3, }, { id: 2, score: 1, }, { id: 3, score: 0, }] }, { user: 'B', answers: [{ id: 1, score: 2, }, { id: 2, score: 1, }, { id: 3, score: 0, }] },],

  lowScore = { count: Infinity },
  highScore = { count: -Infinity };

Object
  .entries(
    input
      .flatMap(({ answers }) => answers)
      .reduce((obj, cur) => {
        !obj[cur.id] ? obj[cur.id] = cur.score : obj[cur.id] += cur.score
        return obj
      }, {})
  )
  .forEach(([id, count]) => {
    // update low count
    if (count < lowScore.count) {
      lowScore.count = count;
      lowScore.id = id;
    }
    // update high count
    if (count > highScore.count) {
      highScore.count = count;
      highScore.id = id;
    }
  });

console.log({ lowScore, highScore })

【讨论】:

    【解决方案2】:

    // sample data
    let data = [{
            user: 'A',
            answers: [{
                    id: 1,
                    score: 1,
                },
                {
                    id: 2,
                    score: 2,
                },
                {
                    id: 3,
                    score: 3,
                },
                {
                    id: 4,
                    score: 4,
                },
            ]
        },
        {
            user: 'B',
            answers: [{
                    id: 1,
                    score: 1,
                },
                {
                    id: 2,
                    score: 2,
                },
                {
                    id: 3,
                    score: 3,
                },
                {
                    id: 4,
                    score: 4,
                },
            ]
        },
    ]
    
    
    let scoreSum = []; //scoreSum to store  total score of each question
    let initialValue = 0;
    
    for (let i = 0; i < 4; i++) {
        let sum = data.reduce(function (accumulator, currentValue) {
            return accumulator + currentValue.answers[i].score;
        }, initialValue)
    
        scoreSum.push(sum);
    }
    
    let highestScore = Math.max(...scoreSum);
    let lowestScore = Math.min(...scoreSum);
    // increasing index by 1 to match with question numbers
    let highestScoreIndex = scoreSum.indexOf(highestScore) + 1;
    let lowestScoreIndex = scoreSum.indexOf(lowestScore) + 1;
    
    
    // Array.prototype.getDuplicates  returns an object where the keys are the duplicate entries 
    // and the values are an array with their indices.
    Array.prototype.getDuplicates = function () {
        var duplicates = {};
        for (var i = 0; i < this.length; i++) {
            if (duplicates.hasOwnProperty(this[i])) {
                duplicates[this[i]].push(i);
            } else if (this.lastIndexOf(this[i]) !== i) {
                duplicates[this[i]] = [i];
            }
        }
    
        return duplicates;
    };
    
    
    let sameScore = scoreSum.getDuplicates();
    
    // checking if highest score has duplicates
    // and if so then updaing highest score index
    //with highest score indices
    if (sameScore.hasOwnProperty(highestScore)) {
        highestScoreIndex = sameScore[highestScore].map((a) => a + 1);
    }
    // checking if lowest score has duplicates
    // and if so then updaing lowest score index
    //with lowest score indices
    if (sameScore.hasOwnProperty(lowestScore)) {
        lowestScoreIndex = sameScore[lowestScore].map((a) => a + 1);
    }
    
    
    console.log(`Top question no(s): ${highestScoreIndex}  highest score:${highestScore}`);
    console.log(`bottom question no(s): ${lowestScoreIndex}  lowest score:${lowestScore}`);

    【讨论】:

      【解决方案3】:

      对于每个 user,我只使用嵌套的 reduce.answers 中的每个 answer 循环一次。

      input 数组有三个用户,每个用户有三个答案。

      let input = [{ user: 'A', answers: [{ id: 1, score: 2, }, { id: 2, score: 1, }, { id: 3, score: 0, }] }, { user: 'B', answers: [{ id: 1, score: 2, }, { id: 2, score: 4, }, { id: 3, score: 0, }] }, { user: 'c', answers: [{ id: 1, score: 0, }, { id: 2, score: 3, }, { id: 3, score:5, }] }]
      
      function showBestAndWorstFrom(input) {
        let highestScore = {'id': 0, 'score': -Infinity};
        let lowestScore =  {'id': 0, 'score': Infinity};
        let currentScore = 0;
        let id = 0;
        const LAST_USER = input.length - 1;
      
        let answers = input.reduce((combinedObj, user, userIndex) => {
          return user.answers.reduce((_answerObj, _answer) => {
            id = _answer.id
            currentScore = (_answerObj[id] || 0) + _answer.score;
            _answerObj[id] = currentScore;
            
            if (userIndex == LAST_USER) {
              highestScore = (highestScore.score < currentScore) ? {'id': id, 'score': currentScore } : highestScore;     
              lowestScore  = (lowestScore.score  > currentScore) ? {'id': id, 'score': currentScore } : lowestScore;
            }
          
            return _answerObj;
          }, combinedObj);
        }, {});
        
        // console.log(answers); // { "1": 4, "2": 8, "3": 5 }
        
        return {highestScore, lowestScore};
      }
      
      console.log(showBestAndWorstFrom(input))

      【讨论】:

        猜你喜欢
        • 2018-01-03
        • 1970-01-01
        • 2017-02-26
        • 1970-01-01
        • 1970-01-01
        • 2021-10-01
        • 2022-01-18
        相关资源
        最近更新 更多