Monday, March 13, 2017

Group by my json object and get the count of each group

Leave a Comment

I am working with 2 rest api services. the first one will get items from a list named "Main category" as follow:-

/test/_api/web/lists/getbytitle('MainCategory')/items?$select=Title 

and it will return the following json:-

{     "d": {         "results": [             {                                     "FileSystemObjectType": 0,                 "Id": 15,                 "ID": 15,                 "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",                 "Title": "A",                 "Modified": "2017-03-01T12:08:35Z",                 "Created": "2017-03-01T12:08:35Z",                 "AuthorId": 1,                 "EditorId": 1,                 "OData__UIVersionString": "1.0",                 "Attachments": false,                 "GUID": "c93d9db6-efd8-4b49-9024-f8cc30c0a48e"             },             {                  "FileSystemObjectType": 0,                 "Id": 16,                 "ID": 16,                 "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",                 "Title": "B",                 "Modified": "2017-03-01T12:10:27Z",                 "Created": "2017-03-01T12:10:27Z",                 "AuthorId": 1,                 "EditorId": 1,                 "OData__UIVersionString": "1.0",                 "Attachments": false,                 "GUID": "a023853c-e967-4db8-8414-a7c390dcc09a"             }, 

and i have another list named Pages, where each page is linked to a main category item.

/test/_api/web/lists/getbytitle('pages')/items??$select=Title,MainCategory&$filter=MainCategory eq 

here is the return json:-

{     "d": {         "results": [             {                     "Title": "Home",                 "MainCategory": "A"             },             {                     "Title": "ewrwe",                 "MainCategory": "A"             },             {                     "Title": "tgtg",                 "MainCategory": "A"             },             {                     "Title": "dsdfsfdfsdfsdfsdfsdsdfsd",                 "MainCategory": "A"             },             {                     "Title": "jk",                 "MainCategory": "A"             }         ]     } } 

now i want to show a table which show the number of pages under each category. so i implemented the following, which do the following:-

  1. get all the main categories.
  2. for each main category do another call to the pages list to get the related pages and count the result.

here is my script:-

<script> $(document).ready(function() {   function getCurrentMainCategoryID() {         var results = "";     var html = "";     html = "<table style='margin-left:20%;width:50%;boarder:3px solid'><th style='text-align:left'>Main Category</th><th></th><th style='text-align:left'>Pages</th>";     var url = "/test/_api/web/lists/getbytitle('MainCategory')/items?$select=Title";      $.ajax({       url: url,       method: "GET",       async: false,       headers: {         "Accept": "application/json; odata=verbose"       },       success: function(data) {         if (data.d.results.length > 0) {               results = data.d.results;           for (var j2 = 0; j2 < results.length; j2++) {                 var currentMainCatTitle = results[j2].Title;              $.ajax({               url: "/test/_api/web/lists/getbytitle('pages')/items??$select=Title,MainCategory&$filter=MainCategory eq " + "'" + currentMainCatTitle + "'",               method: "GET",               async: false,               headers: {                 "Accept": "application/json; odata=verbose"               },               success: function(data) {                 if (data.d.results.length > 0) {                   html = html + "<tr><td>" + "</td>" + "<td>  </td><td>" + data.d.results.length + "</td></tr>";                 }               },               error: function(data) {               }             });           }             }           },       error: function(data) {       }     });     $("#inserthere").after(html + "</table>");      return results;   }    getCurrentMainCategoryID(); }); </script> 

now my script will show the correct result. but i am facing a performance issue, since i am initiating a separate call for each main category. so i am not sure if i can modify my script to do the following:-

  1. get all the main categories (as i am currently doing it).
  2. get all the pages at once. instead of separately getting the pages for each main category ..
  3. inside the script to group the pages according to their main category. and store the results inside a temp variable
  4. loop through the main category items, and for each main category item get its count from the temp variable in step 3...

so can anyone advice on this please? how i can group my json based on the main category?

4 Answers

Answers 1

If you want to make the minimum number of ajax calls you will need to make just two, as you say (although you could do it with only one if you really, really wanted). The key is to nest them: so the success of the first triggers the second, and then the successful response of the second displays the results.

I'd do it something like the following. Here I'm just displaying the GUID from the Main Categories, and the count of the matching Pages, which, if I understand correctly, is essentially what you're after.

var mainCategorySimulatedResponse = {    "d": {      "results": [{        "FileSystemObjectType": 0,        "Id": 15,        "ID": 15,        "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",        "Title": "A",        "Modified": "2017-03-01T12:08:35Z",        "Created": "2017-03-01T12:08:35Z",        "AuthorId": 1,        "EditorId": 1,        "OData__UIVersionString": "1.0",        "Attachments": false,        "GUID": "c93d9db6-efd8-4b49-9024-f8cc30c0a48e"      }, {        "FileSystemObjectType": 0,        "Id": 16,        "ID": 16,        "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",        "Title": "B",        "Modified": "2017-03-01T12:10:27Z",        "Created": "2017-03-01T12:10:27Z",        "AuthorId": 1,        "EditorId": 1,        "OData__UIVersionString": "1.0",        "Attachments": false,        "GUID": "a023853c-e967-4db8-8414-a7c390dcc09a"      }]    }  }    var pagesSimulatedResponse = {    "d": {      "results": [{        "Title": "Home",        "MainCategory": "A"      }, {        "Title": "ewrwe",        "MainCategory": "A"      }, {        "Title": "tgtg",        "MainCategory": "A"      }, {        "Title": "dsdfsfdfsdfsdfsdfsdsdfsd",        "MainCategory": "A"      }, {        "Title": "jk",        "MainCategory": "A"      }, {        "Title": "Cliff Curtis",        "MainCategory": "B"      }, {        "Title": "Martin Henderson",        "MainCategory": "B"      }]    }  }      $(document).ready(function() {      function showMainCategoryData() {        var mainCategories, pages;        /*      // Make nested ajax calls here, one for Main Categories, the other for Pages.      $.ajax({        url: url,        ...        success: function(data) {            if (data.d.results.length > 0) {            // store Main Categories data            mainCategories = data.d.results;              // call nested `Pages` ajax (only runs with Main Categories successful response)            $.ajax({              url: url,              ...              success: function(data) {                if (data.d.results.length > 0) {                  // store Pages data                  pages = data.d.results;                    // display results                  displayResults();                  } else {                  console.log('Pages returned no data');                }              },              error: function(data) {}            });          } else {            console.log('MainCategories returned no data');          }        },        error: function(data) {}      });       */        function displayResults() {        var results = "";        var html = "<table class='mainCatTable'><th class='mainCatHdr'>Main Category</th><th class='mainCatHdr'>Pages</th>";          //loop over Main Categories        for (var catIdx = 0; catIdx < mainCategories.length; catIdx++) {            var pageCount = 0;            for (var pageIdx in pages) {            if (pages[pageIdx].MainCategory == mainCategories[catIdx].Title)              pageCount++;          }            html += "<tr><td class='padRight'>" + mainCategories[catIdx].GUID + "</td>" + "<td>" + pageCount + "</td></tr>";        }          $("#inserthere").append(html + "</table>");      }        /*************** emulation code only ***************/      // emulate above ajax calls          function ajaxCatSuccess(data) {        if (data.d.results.length > 0) {          // store Main Categories data          mainCategories = data.d.results;            // emulate call to nested `Pages` ajax          ajaxPageSuccess(pagesSimulatedResponse);          } else {          console.log('MainCategories returned no data');        }      }        function ajaxPageSuccess(data) {        if (data.d.results.length > 0) {          // store Pages data          pages = data.d.results;            // display results          displayResults();          } else {          console.log('Pages returned no data');        }      }        // trigger first simulated ajax call        ajaxCatSuccess(mainCategorySimulatedResponse)      /************ end of emulate code ***************/      }      showMainCategoryData();    });
.mainCatTable {    margin-left: 10%;    width: 80%;    border: 3px solid  }    .mainCatHdr {    text-align: left  }    .padRight {    padding-right: 10px;  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <div id="inserthere"></div>

Answers 2

Maybe cleaning up your code will help? What I had in mind was get all the calls you need to make into one array and then map the api calls asynchronously then you'll get back all the pages faster. You need to make the the mapping of getPages happen in parallel.

<script> $(document).ready(function() {    function getCurrentMainCategoryID() {      var results = "";     var html = "<table style='margin-left:20%;width:50%;boarder:3px solid'><th style='text-align:left'>Main Category</th><th></th><th style='text-align:left'>Pages</th>";     var url = "/test/_api/web/lists/getbytitle('MainCategory')/items?$select=Title";      $.ajax({       url: url,       method: "GET",       async: false,       headers: {         "Accept": "application/json; odata=verbose"       },       success: function(data) {          var getTitle = result => result.Title;         var results = data.d.results;         var titles = results.map(getTitle);          // not sure if it will be done in parallel         // look for a library async.js vs parallel.js ???         titles.map(title => getPages(title))         }     });      $("#inserthere").after(html + "</table>");      return results;   });    function getPages(title) {     $.ajax({       url: "/test/_api/web/lists/getbytitle('pages')/items??$select=Title,MainCategory&$filter=MainCategory eq " + "'" + title + "'",       method: "GET",       async: false,       headers: {         "Accept": "application/json; odata=verbose"       },       success: function(data) {         if (data.d.results.length > 0) {           html = html + "<tr><td>" + "</td>" + "<td>  </td><td>" + data.d.results.length + "</td></tr>";         }       }     });   }    getCurrentMainCategoryID(); }); </script> 

if you want to group elements into an array of objects just update your 2nd ajax call to create an object of the data and in the first one map over the mainCat's to create an array of desired grouping:

$(document).ready(function() {    function getCurrentMainCategoryID() {      var grouped;     var html = "<table style='margin-left:20%;width:50%;boarder:3px solid'><th style='text-align:left'>Main Category</th><th></th><th style='text-align:left'>Pages</th>";     var url = "/test/_api/web/lists/getbytitle('MainCategory')/items?$select=Title";      $.ajax({       url: url,       method: "GET",       async: false,       headers: {         "Accept": "application/json; odata=verbose"       },       success: function(data) {         var getTitle = result => result.Title;         var results = data.d.results;         var titles = results.map(getTitle);          grouped = titles.map(title => {           return {title: title, results: getPages(title)}         });          console.log(grouped);       }     });      $("#inserthere").after(html + "</table>");      return grouped;   });    function getPages(title) {      var resultsObject;      $.ajax({       url: "/test/_api/web/lists/getbytitle('pages')/items??$select=Title,MainCategory&$filter=MainCategory eq " + "'" + title + "'",       method: "GET",       async: false,       headers: {         "Accept": "application/json; odata=verbose"       },       success: function(data) {         resultsObject = {           pages: data.d.results,           length: data.d.results.length         }       }     });      return resultsObject;   }    getCurrentMainCategoryID(); }); 

sample grouping

[   {     title: _,     results: {       pages: [...],       length: n     }   }, {     title: _,     results: {       pages: [...],       length: n     }   }, ] 

If you get all the data back in one call you can query for mainCategories and group.

sample code for grouping

function getMainCat(data, cat) {    let grouped = data.reduce((acc, item) => {      let selection = Object.keys(item).reduce((acc, key) => {          let results = item[key].results;        let pages = results.filter(result => result["MainCategory"] == cat);          let select = {          title: key,          results: {            pages: pages,            length: pages.length          }        }          return acc.concat(select);      }, []);        return acc.concat(selection);    }, []);      return grouped;  }    let data = [    {      "d": {        "results": [          {            "Title": "Home",            "MainCategory": "A"          }, {            "Title": "ewrwe",            "MainCategory": "A"          }, {            "Title": "tgtg",            "MainCategory": "A"          }, {            "Title": "dsdfsfdfsdfsdfsdfsdsdfsd",            "MainCategory": "A"          }, {            "Title": "jk",            "MainCategory": "B"          }        ]      }    }  ]    console.log("searching A: ");  console.log(getMainCat(data, 'A'));  console.log("searching B: ");  console.log(getMainCat(data, 'B'));  console.log("searching C: ");  console.log(getMainCat(data, 'C'));

That's a suggestion overall I like how this code cleans up !

Answers 3

Some suggestions :

  • Instead of calling Pages API call every time based on the mainCategory API call response. You can call both the API's(mainCategory & pages) only once to increase the performance of the application.
  • You can call both the API's asynchronously to get the data on page load.

    $(document).ready(function() {   function getCurrentMainCategoryID() { var url = "/test/_api/web/lists/getbytitle('MainCategory')/items?$select=Title"; $.ajax({   url: url,   method: "GET",   headers: {     "Accept": "application/json; odata=verbose"   },   success: function(data) {     if (data.d.results.length > 0) {       var mainCategoriesData = data.d.results;       getPagesData();     } else {       console.log('MainCategories returned no data');     }      function getPagesData() {       var url = "/test/_api/web/lists/getbytitle('pages')/items";       $.ajax({         url: url,         method: "GET",         headers: {           "Accept": "application/json; odata=verbose"         },         success: function(data) {           if (data.d.results.length > 0) {             var pagesData = data.d.results;           } else {           console.log('Pages returned no data');           }         }     });     }   } }); 

Now, you have both pages data and main category data.

var mainCategoriesData = [             {                                     "FileSystemObjectType": 0,                 "Id": 15,                 "ID": 15,                 "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",                 "Title": "A",                 "Modified": "2017-03-01T12:08:35Z",                 "Created": "2017-03-01T12:08:35Z",                 "AuthorId": 1,                 "EditorId": 1,                 "OData__UIVersionString": "1.0",                 "Attachments": false,                 "GUID": "c93d9db6-efd8-4b49-9024-f8cc30c0a48e"             },             {                  "FileSystemObjectType": 0,                 "Id": 16,                 "ID": 16,                 "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",                 "Title": "B",                 "Modified": "2017-03-01T12:10:27Z",                 "Created": "2017-03-01T12:10:27Z",                 "AuthorId": 1,                 "EditorId": 1,                 "OData__UIVersionString": "1.0",                 "Attachments": false,                 "GUID": "a023853c-e967-4db8-8414-a7c390dcc09a"             }];  var pagesData = [             {                     "Title": "Home",                 "MainCategory": "A"             },             {                     "Title": "ewrwe",                 "MainCategory": "A"             },             {                     "Title": "tgtg",                 "MainCategory": "A"             },             {                     "Title": "dsdfsfdfsdfsdfsdfsdsdfsd",                 "MainCategory": "A"             },             {                     "Title": "jk",                 "MainCategory": "A"             }         ]; 

Using for ... in loop you can iterate the response and get the page counts as per the main category.

DEMO

var mainCategoriesData = [              {                                      "FileSystemObjectType": 0,                  "Id": 15,                  "ID": 15,                  "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",                  "Title": "A",                  "Modified": "2017-03-01T12:08:35Z",                  "Created": "2017-03-01T12:08:35Z",                  "AuthorId": 1,                  "EditorId": 1,                  "OData__UIVersionString": "1.0",                  "Attachments": false,                  "GUID": "c93d9db6-efd8-4b49-9024-f8cc30c0a48e"              },              {                   "FileSystemObjectType": 0,                  "Id": 16,                  "ID": 16,                  "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",                  "Title": "B",                  "Modified": "2017-03-01T12:10:27Z",                  "Created": "2017-03-01T12:10:27Z",                  "AuthorId": 1,                  "EditorId": 1,                  "OData__UIVersionString": "1.0",                  "Attachments": false,                  "GUID": "a023853c-e967-4db8-8414-a7c390dcc09a"              }];    var pagesData = [              {                      "Title": "Home",                  "MainCategory": "A"              },              {                      "Title": "ewrwe",                  "MainCategory": "A"              },              {                      "Title": "dkjfkdf",                  "MainCategory": "B"              },                          {                      "Title": "tgtg",                  "MainCategory": "A"              },              {                      "Title": "dsdfsfdfsdfsdfsdfsdsdfsd",                  "MainCategory": "A"              },              {                      "Title": "slkfjrel",                  "MainCategory": "B"              },                          {                      "Title": "jk",                  "MainCategory": "A"              }          ];			            var resArr = [];    var count = 1;  for (var i in mainCategoriesData) {    for (var j in pagesData) {      if(pagesData[j].MainCategory == mainCategoriesData[i].Title) {        mainCategoriesData[i].pagesCount = count++;      }    }    count = 1;    resArr.push({      "Main Category": mainCategoriesData[i].Title,      "PagesCount": mainCategoriesData[i].pagesCount    })  }            console.log(resArr);

Answers 4

Use jQuery.getJSON() it will return the object already parsed. You only need two ajax calls. Modify the url and remove the filter for category you want to get all pages at one call.

var results = []; var html = "<table style='margin-left:20%;width:50%;boarder:3px solid'><th style='text-align:left'>Main Category</th><th></th><th style='text-align:left'>Pages</th>";  $.getJSON( "/test/_api/web/lists/getbytitle('MainCategory')/items?$select=Title")   .done(function( categories ) {     var mainCategoryResults = categories.d.results;     $.getJSON( "/test/_api/web/lists/getbytitle('pages')/items?$select=Title,MainCategory")       .done(function( pages ) {         var pageResults = pages.d.results;               $(mainCategoryResults).each(function(k,v){             var i=0;             $(pageResults).each(function(key,val){                 if(v['Title'] === val['MainCategory']){                     i++;                 }             });         results.push([v.Title,i]);         });              })       .fail(function( jqxhr, textStatus, error ) {         var err = textStatus + ", " + error;         console.log( "Pages Request Failed: " + err );     });        })   .fail(function( jqxhr, textStatus, error ) {     var err = textStatus + ", " + error;     console.log( "Categories Request Failed: " + err ); }); 

You can test the double each() loop that creates the result in the below snippet.

var a = {    "d": {      "results": [{          "FileSystemObjectType": 0,          "Id": 15,          "ID": 15,          "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",          "Title": "A",          "Modified": "2017-03-01T12:08:35Z",          "Created": "2017-03-01T12:08:35Z",          "AuthorId": 1,          "EditorId": 1,          "OData__UIVersionString": "1.0",          "Attachments": false,          "GUID": "c93d9db6-efd8-4b49-9024-f8cc30c0a48e"        },        {          "FileSystemObjectType": 0,          "Id": 16,          "ID": 16,          "ContentTypeId": "0x010006DDBDD3BDC9914B92E911733F2A56E1",          "Title": "B",          "Modified": "2017-03-01T12:10:27Z",          "Created": "2017-03-01T12:10:27Z",          "AuthorId": 1,          "EditorId": 1,          "OData__UIVersionString": "1.0",          "Attachments": false,          "GUID": "a023853c-e967-4db8-8414-a7c390dcc09a"        }      ]    }  };  var b = {    "d": {      "results": [{          "Title": "Home",          "MainCategory": "A"        },        {          "Title": "ewrwe",          "MainCategory": "A"        },        {          "Title": "tgtg",          "MainCategory": "A"        },        {          "Title": "dsdfsfdfsdfsdfsdfsdsdfsd",          "MainCategory": "A"        },        {          "Title": "jk",          "MainCategory": "A"        }      ]    }  };  var c = [];  $(a.d.results).each(function(k, v) {    var i = 0;    $(b.d.results).each(function(key, val) {      if (v['Title'] === val['MainCategory']) {        i++;      }    });    c.push([v.Title, i]);  });  console.log(c);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment