0

I am using a API to generate the average time spent visiting a webpage and then dividing the sum of all returned values by the number of days set in the API.

let avgtime = 0;

for (i in data) {
  if (data[i].avg_time_on_site) {
    avgtime += data[i].avg_time_on_site;
  }
}
$("#avgtime").html(avgtime / datePeriod);

Whereas datePeriod is the total amount of days returned by the API. For example, if the datePeriod is set to 30 it would return the last 30 days of avgtime.

The snippet of avgtime += data[i].avg_time_on_site; returns all of avg_time_on_site from JSON as a sum and is divided by datePeriod to get the average over the set amount of days $("#avgtime").html(avgtime / datePeriod);.

My issue, however, is that this only works if a webpage has a return value for each and every day. If the webpage is not visited on a single day as shown in the example JSON below, avgtime will not be correct because it is dividing the sum of all return values by the total days, not the total amount of days that have rows.

{
   "2017-12-30":[],
   "2017-12-31":{
      "nb_uniq_visitors":1,
      "nb_visits":1,
      "avg_time_on_site":41,
   },
   "2018-01-01":{
      "nb_uniq_visitors":1,
      "nb_visits":2,
      "avg_time_on_site":52,
   },
   "2018-01-02":[],
   "2018-01-03":{
      "nb_uniq_visitors":1,
      "nb_visits":3,
      "avg_time_on_site":83,
   },
   "2018-01-04":[]
}

I would like to replace the function datePeriod from $("#avgtime").html(avgtime / datePeriod); with another that counts the total rows in JSON, excluding any days that do not have values.

I have tried researching the solution first but am at a loss. Thank you for your help.

Here is my full script

setTimeout(function() {
  $('#DateSelector').val('30').trigger("change")
}, 1000);

function datePeriod() {
  let datePeriod = $("#DateSelector").val();
  $("#DateShow").html(datePeriod);
  $.getJSON(`https://discovrbookings.innocraft.cloud/?module=API&method=API.get&format=json&idSite=2&period=day&date=last${datePeriod}&token_auth=68aa5bd12137f13255dcb98794b65dff`, (data) => {
    //console.log(data);
    let avgtime = 0;

    for (i in data) {
      if (data[i].avg_time_on_site) {
        avgtime += data[i].avg_time_on_site;
      }
    }
    $("#avgtime").html(avgtime / datePeriod);
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select name="DateSelector" id="DateSelector" onchange="datePeriod();">
   <option value="7">Last 7 Days</option>
   <option value="14">Last 14 Days</option>
   <option value="30">Last 30 Days</option>
   <option value="90">Last 90 Days</option>
   <option value="365">Last 365 Days</option>
</select>

<h2 id="avgtime"></h2>

5
  • Just keep your own count inside your if block. Init a second var to 0 before the loop, then increase by one whenever you add to avgtime. Commented Jan 7, 2018 at 21:20
  • @ChrisG. Sorry, I don't quite understand. Are you suggesting, I take data[i].avg_time_on_site and divide it by the length of avg_time_on_site. So to count only the avg_time_on_site? I had tried to perform the sum inside of here but couldn't get a valid response. Commented Jan 7, 2018 at 21:25
  • The answer below shows what I meant. Commented Jan 7, 2018 at 21:27
  • Note that this is a very odd average. This is the average average-time-on-site over that period. Wouldn't you prefer either the average user's time on site or the average visit time on site for that period? The latter might looks like (0 + (1 * 41) + (2 * 52) + 0 + (3 * 83) + 0) / (0 + 1 + 2 + 0 + 3 + 0) => 249 / 6 ~> 185 Commented Jan 7, 2018 at 22:24
  • Or it looks like you could use sum_visit_length from the API. Commented Jan 7, 2018 at 22:25

2 Answers 2

1

I would recommend you just keep track of the days in your loop with another variable. That way you are independent on the datePeriod.

let avgtime = 0;
//another variable, because you can't use datePeriod
let dayCount = 0;

for (i in data) {
  if (data[i].avg_time_on_site) {
    avgtime += data[i].avg_time_on_site;
    dayCount++;
  }
}
$("#avgtime").html(avgtime / dayCount);
Sign up to request clarification or add additional context in comments.

7 Comments

That is perfect. Thank you and once able I will mark as the correct answer.
an addition of hasOwnProperty will improve it further.
I would agree, in addition it also completely eliminated the need for 'datePeriod' variable.
@AGoranov no datePeriod is used in his getJSON-Request
@Christian Ah, thank you for the correction. It still would be better not to be dependent on the value checked when calculating the average. This should solely be done inside the loop with an additional variable.
|
0

I would suggest a different approach. Normalize the data using map, and then reduce it to an aggregate object before finally dividing the total time by the number of visits.

See my note on the question about the odd average being used. Here I supply one that gives the average time spent per visit over the dates supplied.

const avg = visits => (({cnt, time}) => time / cnt)(Object.values(visits).map(
  // Normalize to [{cnt: 0, avg: 0}, {cnt: 1, avg: 41}, ...]
  day => ({cnt: day.nb_visits || 0, avg: day.avg_time_on_site || 0})
).reduce(
  // combine to {count: 6, time: 394}
  (totals, day) => ({cnt: totals.cnt + day.cnt, time: totals.time + day.cnt * day.avg}), 
  {cnt: 0, time: 0}
))

avg(visits) //=> 65.666666666667

This could be somewhat simplified by using sum_visit_date instead of avg_time_on_site: you could skip the multiplication. But that's a minor change.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.