5
\$\begingroup\$

Would love feedback on a vanilla JS challenge I've recently completed. It is a method for calculating and ranking scores. If possible, I would love to make it better. Any suggestions welcome.

Challenge Directions: The input array contains a series of objects that hold user scores. Scores are ranked both individually (within the object) as well as against the other score objects to find overall ranking.

MY CODE:

//STEP 1 - CALCULATE TOTAL SCORE & SCORE RANKS FOR EACH OBJECT
function calculateResults(input) {
    return input.map((scoreSeries) => {
        let final_rank = 0;
        calculatedScoreSeries = {...scoreSeries}
          
        Object.keys(scoreSeries).forEach((key) => {
          if (key !== 'category') {
            final_rank = final_rank + scoreSeries[key]  //Adding up the score.
            calculatedScoreSeries[key] = calculateSourceRank(scoreSeries, key) //calculating the rank
          }})
        calculatedScoreSeries.final_rank = final_rank //adding total_score prop to the new object
        return calculatedScoreSeries
    })
}
//to calculate the ranking of each source
function calculateSourceRank(scoreSeries, key) {
    let rank = 1;
    for (let i = 1; i < Object.keys(scoreSeries).length; i++) {
      if (scoreSeries[Object.keys(scoreSeries)[i]] > scoreSeries[key]) {
        rank++
      }
    } 
   return rank
}

//STEP 2 - COMPARE OBJECTS FOR FINAL RANK
function calculateFinalRank(calculatedResults) {
    return calculatedResults.map((calculatedScoreSeries) => {
        let rankedScoreSeries = {...calculatedScoreSeries}
        let rank = 1;
        for (let i = 0; i < calculatedResults.length; i++) {
            if (calculatedResults[i].final_rank > calculatedScoreSeries.final_rank) {
            rank++
            }
          rankedScoreSeries.final_rank = rank
        } 
    return rankedScoreSeries
  })
}

let output = calculateFinalRank(calculateResults(input))
console.log(output)

TEST INPUTS W EXPECTED OUTPUTS:

//each object's score sources are ranked (if two are equal they share the higher ranking).
//then all objects are compared for the overall rnking (if two are equal they share the higher ranking)
input = [ // test input 1
    {
    "category":"test3",
    "source1":50,
    "source2":100,
    "source3":30,
    "source4":10,
    "source5":10,
    },
    
    {
    "category":"test4",
    "source1":100,
    "source2":30,
    "source3":10,
    "source4":10,
    "source5":50,
    },

    {
    "category":"test5",
    "source1":5,
    "source2":10,
    "source3":10,
    "source4":40,
    "source5":5,
    },
    ]
    
    output = [
    {
    "category":"test3",
    "source1":2
    "source2":1
    "source3":3
    "source4":4
    "source5":4
    "final_rank":1
    },
    
    {
    "category":"test4",
    "source1":1
    "source2":3
    "source3":4
    "source4":4
    "source5":2
    "final_rank":1
    }, 

    {
    "category":"test5",
    "source1":4,
    "source2":2,
    "source3":2,
    "source4":1,
    "source5":4,
    "final_rank":3
    },
    ]

    input = [ // test input 2
    {
    "category":"cat1",
    "src1":20,
    "src2":30,
    "src3":40,
    "src4":50,
    "src5":50 
    },

    {
    "category":"cat1",
    "src1":10, 
    "src2":0, 
    "src3":20,
    "src4":20, 
    "src5":100 
    },
    ]

    output = [ 
    {
    "category":"cat1",
    "src1":5,
    "src2":4,
    "src3":3,
    "src4":1,
    "src5":1,
    "final_rank":1
    },

    {
    "category":"cat1",
    "src1":4,
    "src2":5,
    "src3":2,
    "src4":2,
    "src5":1,
    "final_rank":2
    },
  ]
   

BUSINESS LOGIC:

1 - Category will always have at least one 'category'. More than 1 catgeory will be saved as an array under the category key. Category will always be the first key.

2 - each object in the input array will have an equal number of sources (although this number can be any amount)

3 - if a total score is equal to anothers, then they will share the higher ranking (same as individual score ranking)

\$\endgroup\$
  • 1
    \$\begingroup\$ If this is a programming challenge from an online site it would be nice if the text of the challenge as well as a link to the challenge was posted as part of the question. \$\endgroup\$ – pacmaninbw Jan 27 at 15:11
  • \$\begingroup\$ @pacmaninbw this is all the info, not an online one I can link to, unfortunately. The idea was to look at the input data and determine what is happening yourself, and then build business logic based on your findings (which I've outlined for my particular case). \$\endgroup\$ – gracie catherine Jan 27 at 16:51
3
\$\begingroup\$

Good things

I like the functional approach taken with this code, and that some features like arrow functions are used.

Suggestions

const vs let

It would be wise to default to using const for any variable that doesn't need to be re-assigned. If you later determine a value should be re-assigned then switch it to using let. This helps prevent accidental re-assignment in the future.

Use consistent indentation

Some lines appear to be indented with two spaces, while others are indented with four. It is wise to use uniform indentation throughout the code.

Use consistent line terminators

Many lines are terminated with a semi-colon but some are not. Unless you completely understand rules of Automatic semicolon insertion or are using a compiler/module bundler it is best to include line terminators.

multiple calls to Object.keys() in loop

Let us take a look at the following block:

for (let i = 1; i < Object.keys(scoreSeries).length; i++) {
  if (scoreSeries[Object.keys(scoreSeries)[i]] > scoreSeries[key]) {
    rank++
  }
} 

For each iteration of the loop, Object.keys() is called twice. That function could be called once if the result is stored in a variable.

const scoreKeys = Object.keys(scoreSeries)
for (let i = 1; i < scoreKeys.length; i++) {
  if (scoreSeries[scoreKeys[i]] > scoreSeries[key]) {
    rank++
  }
} 

The syntax could be simplified using a for...of loop:

const scoreKeys = Object.keys(scoreSeries)
for (const scoreKey of scoreKeys) {
  if (scoreSeries[scoreKey] > scoreSeries[key]) {
    rank++
  }
} 

The whole function could be simplified with a more function approach using .filter():

//to calculate the ranking of each source
function calculateSourceRank(scoreSeries, key) {
    const scoreKeys = Object.keys(scoreSeries)
    return 1 + scoreKeys.filter(scoreKey => scoreSeries[scoreKey] > scoreSeries[key]).length
}

A similar approach could be applied to calculateFinalRank()

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.