簡體   English   中英

用於減少對象數組中數據的遞歸(或迭代)解決方案

[英]Recursive (or iterative) solution for reducing data in an array of objects

編寫一個 function 接收顏色戰爭游戲結果列表並返回定義的獲勝者:

a) 獲勝最多的球隊 b) 如果平局,則在並列球隊的子集中獲勝最多的顏色 c) 最終,如果/當不能再減少並列 colors 的集合時,選擇剩余的一個隨機捆綁隊伍

問題:

  1. 我已經編寫了適用於各種不同數據輸入的代碼,但有人告訴我,其中大部分代碼都是頂級和全局范圍的,很少有代碼被寫成 function。我對此感到困惑,需要一些關於如何將其重寫為正確的 function 的指導(因為我有點認為我已經這樣做了,盡管可能不優雅)
  2. 在我的代碼中,我將數據減少了兩倍(根據數據集的需要)——但正如說明所示,在關系的情況下可能需要將代碼減少兩倍以上,並且我需要找到一個遞歸或迭代的解決方案來減少代碼。 我已經花了很多時間來尋找遞歸解決方案,但它卻讓我望而卻步。
const winner = (teamObj) => {
  const tally = {}
  const tiedReduceData = []
  const secondTally = {}
  const tiedFinals=[]

  const pickTeam = (teamObj) => {
    const [[team1, score1], [team2, score2]] = Object.entries(teamObj)
     return score1 === score2 ?
     null : score1 > score2 ? team1 : team2
  }

  for(const teamObj of data) {
    const team = pickTeam(teamObj)
  
    if(team) {
      tally[team] = (tally[team] ?? 0) + 1
    }
  }

  const getMaxArray = Object.keys(tally)
    .filter(x => { 
      return tally[x] == 
        Math.max.apply(null, Object.values(tally))
     })

  const compare = data.forEach( e => {
      if(Object.keys(e).every(i => getMaxArray.includes(i))){
        tiedReduceData.push(e);
      }
  })

  for (const teamObj of tiedReduceData) {
    const team = pickTeam(teamObj)

    if(team) {
      secondTally[team] = (secondTally[team] ?? 0) +1
    }
  }

  const getMaxArraySubset = Object.keys(secondTally)
    .filter(x => {
      return secondTally[x] ==
        Math.max.apply(null, Object.values(secondTally))
    })

  data.forEach( e => {
     if(Object.keys(e).every(i => getMaxArraySubset.includes(i))){
        tiedFinals.push(e);
     }
  })

  const randomWinner = getMaxArraySubset[Math.floor(Math.random() * getMaxArraySubset.length)]

  const allTied = "No Winner. All Tied."


  if (getMaxArray.length === 1){
    return getMaxArray
  } else if (getMaxArray.length === 0){
    return allTied
   } else if( getMaxArraySubset.length === 1 ){
     return getMaxArraySubset  
   } else {
   return randomWinner
   } 
}

console.log(winner(data))

const data = [ 
{magenta: 1, lime: 0},
{magenta: 1, berry: 0},
{berry: 1, magenta: 0},
{berry: 1, sienna: 0},
{sienna: 1, lime: 0},
{sienna: 1, magenta: 0},
{magenta: 0, lime: 1},
{magenta: 0, berry: 1},
{berry: 0, magenta: 1},
{berry: 0, sienna: 1},
{sienna: 0, lime: 1},
]

從某種意義上說,我認為你的評分者很困惑。 這里唯一的全局變量是data和你的winner function。這是完全合適的。

但是有一點需要清理。

const winner = (teamObj) => {
  const tally = {}
  const tiedReduceData = []
  const secondTally = {}
  const tiedFinals=[]

大概就是這個意思。 雖然這些不是全局的,但它們在您的 function 中起着與全局類似的作用,您在此處初始化數據,然后在別處操作,而無需在調用之間傳遞它們。


  const pickTeam = (teamObj) => {
    const [[team1, score1], [team2, score2]] = Object.entries(teamObj)
     return score1 === score2 ?
     null : score1 > score2 ? team1 : team2
  }

這看起來不錯,只是您重用了名稱teamObj —— winner的參數,並且在這里使用它來表示不同的含義。 我可以建議gamecontest或類似的東西嗎? 它也不是作為winner參數的描述性名稱。 也許matchgamestournament


  for(const teamObj of data) {
    const team = pickTeam(teamObj)
  
    if(team) {
      tally[team] = (tally[team] ?? 0) + 1
    }
  }

這與偽全局變量有關。 tally = {}的早期聲明和這個for循環最好結合在一個reduce調用中。


  const getMaxArray = Object.keys(tally)
    .filter(x => { 
      return tally[x] == 
        Math.max.apply(null, Object.values(tally))
     })

三件事。 首先,您可能希望保留一個局部變量,而不是在測試每個參賽者時重新計算最大值。 其次,名稱getMaxArray聽起來像是一個動作,應該是winnerstopTiernextRoundContestants類的東西會更清楚。

三、注意

Math.max.apply(null, Object.values(tally))

可以更好地用現代 JS 編寫為

Math .max (...Object .values (tally))

  const compare = data.forEach( e => {
      if(Object.keys(e).every(i => getMaxArray.includes(i))){
        tiedReduceData.push(e);
      }
  })

此處的名稱compare完全具有誤導性。 您正在做的是過濾游戲,只包括那些兩位參賽者都屬於頂級的游戲。

同樣,刪除偽全局,我們可以寫這樣的東西

const tiedReduceData = data.filter (game => Object.keys (game).every (team => topTier.includes (team)) )

(我不能不getMaxArray更改為topTier 。這個名字真的很糟糕!)


跳過secondTallymaxArraySubset 如果我們正確地進行遞歸,則不需要這些。


  data.forEach( e => {
     if(Object.keys(e).every(i => getMaxArraySubset.includes(i))){
        tiedFinals.push(e);
     }
  })

  const randomWinner = getMaxArraySubset[Math.floor(Math.random() * getMaxArraySubset.length)]

  const allTied = "No Winner. All Tied."

在這里你計算了你可能返回的所有東西,即使你只會返回其中一個。 只有當你知道你將需要它們時才進行這樣的計算更有意義。


  if (getMaxArray.length === 1){
    return getMaxArray
  } else if (getMaxArray.length === 0){
    return allTied
   } else if( getMaxArraySubset.length === 1 ){
     return getMaxArraySubset  
   } else {
   return randomWinner
   } 
}

這里有四個返回條件,所有條件都有不同的結果。 但是這個問題有三個規格。 你確定你的“allTied”場景是可能的嗎?

事實上,在問題說明中,條件 (b) 並沒有真正指定一個贏家,而是指向一個遞歸。 所以是時候談談遞歸了。

如果我們有幾支得分最高的球隊,我們想再次調用我們的 function,但僅限於包含這些特定球隊的比賽——或者這就是我閱讀問題的方式。 麻煩的是這樣做可能不會減少我們的玩家組,比如三場比賽,A打B,B打C,C打A,每局一勝一負。 這就是我們要選擇一個隨機獲勝者的情況。 如果我們減少了玩家數量('lime' 贏了兩次,'magenta'、'sienna' 和 'berry' 各贏了 3 次,所以我們在沒有 'lime' 的情況下再試一次)然后我們進行遞歸調用。 在這里,您需要過濾輸入中的游戲,以排除那些玩家不在頂級的游戲。 上面的代碼展示了進行這種操作的很多能力,所以我把它留給你了。


在查看您的代碼之前,我自己嘗試了這個,並編寫了一個版本,該版本與您的概念有一些重疊,但也與它們有很大不同。 除了靈感,我不建議使用其中任何一個,但這里沒有任何注釋或討論:

 const matchWinner = (match) => { const players = [...new Set (match.flatMap ( game => Object.keys (game) ))] const scores = match.flatMap (Object.entries).reduce ( (a, [t, s]) => ((a [t] = (a[t] || 0) + s), a), {} ) const topScore = Math.max (...Object.values (scores)) const topTier = Object.entries (scores).filter (([k, v]) => v == topScore).map (([k]) => k) return topTier.length == 1? topTier [0]: topTier.length == players.length? topTier [Math.floor (Math.random () * topTier.length)]: matchWinner (match.filter (game => Object.keys (game).every ( contestant => topTier.includes (contestant) ))) } const data = [{magenta: 1, lime: 0}, {magenta: 1, berry: 0}, {berry: 1, magenta: 0}, {berry: 1, sienna: 0}, {sienna: 1, lime: 0}, {sienna: 1, magenta: 0}, {magenta: 0, lime: 1}, {magenta: 0, berry: 1}, {berry: 0, magenta: 1}, {berry: 0, sienna: 1}, {sienna: 0, lime: 1}] console.log (matchWinner (data)) //=> {"magenta":3,"lime":2,"berry":3,"sienna":3} // First call // {"magenta":2,"berry":3,"sienna":2} // Second call (recursively) // "berry" // Sole winner

更新

有了更多數據,我意識到比賽結果不是贏家/輸家,而只是每方的得分。 考慮到這一點,我們可以更改 function 中的分數計算方式。我選擇國際象棋式,贏 1 分,輸 0 分,平局 1/2 分。 很容易將其更改為足球風格的贏 3 分,輸 0 分,平局 1 分,或者實際上更改為以類似方式分配分數的任何其他系統。 這是國際象棋風格的實現。 它應該能夠簡單地放入我的 function:

  const scores = match.reduce ((a, game) => {
    const [[name1, score1], [name2, score2]] = Object .entries (game)
    a [name1] = (a [name1] || 0) + (score1 > score2 ? 1 : score1 < score2 ? 0 : .5)
    a [name2] = (a [name2] || 0) + (score2 > score1 ? 1 : score2 < score1 ? 0 : .5)
    return a
  }, {})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM