0

I need to calculate average and max rating.

From my perspective this should use map and reduce for this purpose, but, I'm not sure if the JSON has the right format, or if it needs any sanitization.

const
  myJSON = 
    [ { "page": 1, "totalPages": 2, "data": 
        [ { "title": "Developer 1", "rating": 4.7} 
        , { "title": "Developer 2", "rating": 7.8} 
    ] } ]
  ;
var Max_JSO = myJSON.reduce( (acc, cur )=> ((acc.rating > cur.rating)? acc : cur) );
var Min_JSO = myJSON.reduce( (acc, cur )=> ((acc.rating < cur.rating)? acc : cur) );

var Avg_JSO = myJSON.reduce( (acc, cur, idx, arr )=> {
  let
    sum = acc.rating + cur.rating,
    no = idx +1;
  if (no === arr.length) { sum = sum / no };
  return { 'name': cur.name, 'rating': sum }
});

console.log ('max =',  Max_JSO)  
console.log ('min =',  Min_JSO)  
max = { page: 1, totalPages: 2, data: 
        [ { title: 'Developer 1', rating: 4.7 } 
        , { title: 'Developer 2', rating: 7.8 } 
        ] 
      } 

min = { page: 1, totalPages: 2, data: 
        [ { title: 'Developer 1', rating: 4.7} 
        , { title: 'Developer 2', rating: 7.8} 
        ] 
      } 

I'm using this code right now, but I get a weird string as an output:

"max =", "{\&quot;page\&quot;:1,\&quot;totalPages\&quot;:5,\&quot;data\&quot;:[{\&quot;title\&quot;:\&quot;Developer 1\&quot;,\&quot;rating\&quot;:4.7},{\&quot;title\&quot;:\&quot;Developer 2\&quot;,\&quot;rating\&quot;:7.8}]}"
 
"min =", "{\&quot;page\&quot;:1,\&quot;totalPages\&quot;:5,\&quot;data\&quot;:[{\&quot;title\&quot;:\&quot;Developer 1\&quot;,\&quot;rating\&quot;:4.7},{\&quot;title\&quot;:\&quot;Developer 2\&quot;,\&quot;rating\&quot;:7.8}]}"

Is there any way to do this better?

Also, I need to not only read rating from data key, but also outside of it, as you can see on var myJSON

Like produce a resulting json like this:

  {
      "Developer 1": 4.7,
      "Developer 2": 7.8,
  }

MIN being Developer 1 and MAX Developer 2

Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
NeoVe
  • 3,857
  • 8
  • 54
  • 134
  • 2
    Do you want max and min rating for each page or for entire myJSON array? – Ravi Kiran May 16 '21 at 11:12
  • 1
    Please provide a better example with input and output. It is difficult to understand here if you want one max value for entire `myJSON` array or for each object inside `myJSON` array. Same goes for average and min as well. – Sunil Chaudhary May 16 '21 at 11:16
  • 1
    your title is **average and max rating** and your code is **min and max rating** where is the logic? – Mister Jojo May 16 '21 at 11:19
  • It is so unclear what exactly do you want... – DecPK May 16 '21 at 11:20
  • 1
    While calculating `max` and `min`. Isn't the conditon should be `myJSON[0].data.reduce` instead of `myJSON.reduce` like `myJSON[0].data.reduce((acc, cur) => (acc.rating > cur.rating ? acc : cur));` – DecPK May 16 '21 at 11:23
  • @RaviKiran Hi, thanks, for the entire myJSON array, will update my question, but that's the idea – NeoVe May 16 '21 at 11:28

4 Answers4

1

Using this answer from Andy Polhill which explains:

Find the object whose property "Y" has the greatest value in an array of objects

This is an example to calculate min, max and average for one page. If you need to do it for multiple pages, you can first calculate local values for each page, then, combine the data and find the general value.

const json = {
  "page": 1,
  "totalPages": 2,
  "data": [
    { "title": "Developer 1", "rating": 4.7 },
    { "title": "Developer 2", "rating": 7.8 }, 
  ]
};

const max = json.data.reduce((prev, current) => (prev.rating < current.rating) ? prev : current);

const min = json.data.reduce((prev, current) => (prev.rating > current.rating) ? prev : current);

let avg = 0;
json.data.forEach((el) => avg += el.rating);
avg /= json.data.length;

console.log(max);
console.log(min);
console.log(avg);
boranseckin
  • 335
  • 2
  • 12
1

This is one way to do it :

  const myJSON = [
    {
      page: 1,
      totalPages: 2,
      data: [
        { title: "Developer 1", rating: 4.7 },
        { title: "Developer 2", rating: 7.8 },
      ],
    },
  ];

  const Data = myJSON[0].data;
  const result = {};
  Data.forEach((entrie, index) => {
    if (index === 0) {
      result.min = entrie.rating;
      result.max = entrie.rating;
      result.avg = entrie.rating;
    }
    if (index !== 0) {
      result.min = result.min < entrie.rating ? result.min : entrie.rating;
      result.max = result.max > entrie.rating ? result.max : entrie.rating;
      result.avg += entrie.rating;
    }
    if (index + 1 === Data.length) {
      result.avg = result.avg / (index + 1);
    }
  });
  console.log(result);

you can also use sort method with custom sort function passed as a parameter

  const myJSON = [
    {
      page: 1,
      totalPages: 2,
      data: [
        { title: "Developer 1", rating: 4.7 },
        { title: "Developer 2", rating: 7.8 },
      ],
    },
  ];

  const Data = myJSON[0].data;
  Data.sort((b, a) => {
    return a.rating - b.rating;
  });
  console.log(Data);

by changing position of a and b you can decide to sort ascending or reverse and then take first element as min or max and last element as max or min.

PS: in case you are getting data from an api you might need to parse the data with JSON.parse() first.

Pooya Estakhri
  • 1,129
  • 1
  • 11
  • 25
1

The main problem you are having in your implementation is that you are failing to account for the nested shape of your myJSON array (thus all the comments asking if you want per page or combined results).

You can combine all of the logic into a single Array#reduce() call and either map() your myJSON array to return aggregate results per page, or use Array#flatMap() to pass an array of all pages data arrays for combined metrics.

  myJSON = [
    { "page": 1, "totalPages": 2, "data": [{ "title": "Developer 1", "rating": 4.7 }, { "title": "Developer 2", "rating": 7.8 }] },
    { "page": 2, "totalPages": 2, "data": [{ "title": "Developer 1", "rating": 2.2 }, { "title": "Developer 2", "rating": 3 }] }
  ],

  aggregateData = data => {
    const { min, max, sum, count } = data.reduce((a, datum) => {
      if (datum.rating > a.max.rating) {
        a.max = { ...datum };
      }
      if (datum.rating < a.min.rating) {
        a.min = { ...datum };
      }
      a.sum += datum.rating;
      a.count += 1;
      return a;
    }, { min: { rating: Infinity }, max: { rating: -Infinity }, sum: 0, count: 0 });

    const average = sum / count;

    return { min, max, average };
  },

  resultAll = aggregateData(myJSON.flatMap(({ data }) => data));
  resultByPage = myJSON.map(({ data }) => aggregateData(data)),

console.log('Combined');
console.log(resultAll);
console.log('\nBy page');
console.log(resultByPage);
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23
1

You can get an array of the developers using flatMap() then iterate that to update a stats object that includes sum of ratings in order to finally calculate average

const devs = myJSON.flatMap(o => o.data)

const stats =  {sum:0, max: devs[0], min: devs[0]};

devs.forEach(dev => {
  const {rating: r} = dev;
  stats.sum += r;
  if (r > stats.max.rating){
      stats.max = dev;
  }else if (r < stats.min.rating){
     stats.min = dev;
  }
});

stats.avg = stats.sum / devs.length;

console.log(stats)
<script>

var myJSON = [
{
    "page": 1,
    "totalPages": 2,
    "data": [{
      "title": "Developer 1",
      "rating": 4.7
  }, {
      "title": "Developer 2",
      "rating": 7.8
  }]
}];

</script>
charlietfl
  • 170,828
  • 13
  • 121
  • 150