Tips on organizing SAPUI5 OData prior to binding it to a control on the client side

Suppose I have an OData list represented in JSON format:

var data = [
 {"category" : "A", "value" : 1, "group" : "x"},
 {"category" : "B", "value" : 2, "group" : "y"},
 {"category" : "C", "value" : 3, "group" : "x"},
 {"category" : "A", "value" : 4, "group" : "y"},
 {"category" : "A", "value" : 5, "group" : "x"}
];

Initially, a filter is applied to select entries with group == x;. The resulting values are:

var data = [
 {"category" : "A", "value" : 1, "group" : "x"},
 {"category" : "C", "value" : 3, "group" : "x"},
 {"category" : "A", "value" : 5, "group" : "x"}
];

The next step involves client-side grouping by category and summing up the values, leading to the following result:

var data = [
 {"category" : "A", "value" : 6, },
 {"category" : "C", "value" : 3, },
];

This grouped data can then be bound to a SAPUI5 control.

If anyone has a generic solution for this grouping issue, please share it.

An example scenario could involve:

var oDataset = new sap.viz.ui5.data.FlattenedDataset({
    dimensions : [ {axis : 1, value : "{category}", name : "Category" } ],
    measures : [ {value : "{value}", name : "Value" } ],
    data : {
        path : "/Data"
    }
});

var oGraph = new sap.viz.ui5.Donut({
    dataset : oDataset, // sap.viz.ui5.data.Dataset
});

Answer №1

This code successfully queries the Northwind Order_Details service for multiple Products using a generic binding, then leverages map reduce to aggregate the Quantity of each order into total Quantity sold. The results are displayed in a column chart.

Check out the jsBin Example

It's worth noting that sap.viz introduces Array.prototype.map and Array.prototype.reduce functions.

var sURI = 'http://services.odata.org/Northwind/Northwind.svc/';
var oDataModel = new sap.ui.model.odata.ODataModel(sURI, true);
oDataModel.setSizeLimit(10000);
var oJSONModel = new sap.ui.model.json.JSONModel({}, 'jmodel');

// Handler for list of contexts
var handler = function(oEvent) {
    var mapCallback = function(oContext) {
        var obj = {};
        obj.ProductID = oContext.getObject().ProductID,
        obj.Quantity = oContext.getObject().Quantity
        return obj;
    };

    var reduceCallback = function(aPrev, oCurr) {
        var aNext = aPrev;
        var bFound = false;

        aNext.forEach(function(item) {
            if (item.ProductID === oCurr.ProductID) {
                bFound = true;
                item.Quantity += oCurr.Quantity;
            }
        })

        if (bFound === false) {
            aNext.push(oCurr);
        }

        return aNext;
    };
    // Release handler
    oBindings.detachChange(handler);

    var aTotals = oEvent.oSource.getContexts().map(mapCallback).reduce(reduceCallback, []);
    oJSONModel.setData({
        'Order_Totals': aTotals
    });
};

// Filtering orders by 3 products
var oFilter1 = new sap.ui.model.Filter("ProductID", sap.ui.model.FilterOperator.EQ, '1');
var oFilter2 = new sap.ui.model.Filter("ProductID", sap.ui.model.FilterOperator.EQ, '68');
var oFilter3 = new sap.ui.model.Filter("ProductID", sap.ui.model.FilterOperator.EQ, '11');
var aFilter = [oFilter1, oFilter2, oFilter3];

// Sorting by ProductID
var oSorter = new sap.ui.model.Sorter("ProductID", false, true);

// Reducing payload by selecting necessary fields
var oSelect = {
    select: 'ProductID,Quantity'
}

var oBindings = oDataModel.bindList("/Order_Details", null, oSorter, aFilter, oSelect);

// Calling OData service and managing results
oBindings.attachChange(handler);
oBindings.getContexts();

var oDataset = new sap.viz.ui5.data.FlattenedDataset({
    dimensions: [{
        axis: 1,
        name: 'ProductID',
        value: "{ProductID}"
    }],
    measures: [{
        name: 'Quantity Sold',
        value: '{Quantity}'
    }],
    data: {
        path: "/Order_Totals"
    }
});

var oColumnChart = new sap.viz.ui5.Column({
    width: "80%",
    height: "400px",
    plotArea: {
        'colorPalette': d3.scale.category20().range()
    },
    title: {
        visible: true,
        text: 'Quantity Sold by Product'
    },
    dataset: oDataset
});

oColumnChart.setModel(oJSONModel);

Answer №2

I've crafted a code for your final outcome, which may not be completely optimized but functions effectively.

Here is the complete code:

var data = [
 {"category" : "A", "value" : 1, "group" : "x"},
 {"category" : "B", "value" : 2, "group" : "y"},
 {"category" : "C", "value" : 3, "group" : "x"},
 {"category" : "A", "value" : 4, "group" : "y"},
 {"category" : "A", "value" : 5, "group" : "x"}
];

function groupBy( array , f )
{
  var groups = {};
  array.forEach( function( o )
  {
    if(o.group=="x")
    {
        var group = JSON.stringify( f(o) );
        groups[group] = groups[group] || [];
        groups[group].push( o );
    }

  });
  return Object.keys(groups).map( function( group )
  {
    return groups[group]; 
  })
}
var groupByGroup = groupBy(data, function(item)
{
  return [item.group];
});
var groupByCategory = groupBy(groupByGroup[0], function(item)
{
  return [item.category];
});
getData(groupByCategory);

function getData(groupByCategory)
{
  var  finalData=[];
    for(var i=0;i< groupByCategory.length;i++)
    {
    var temp=0;
        for(var j=0;j<groupByCategory[i].length;j++)
        {
            temp+=parseInt(groupByCategory[i][j].value);
        }
        finalData.push({"category":groupByCategory[i][0].category, "value":temp})
    }
    console.log(finalData);//final data is your required results
}

If you wish to compare using two parameters, such as category and group, modify the groupByGroup function as follows:

 groupByCategory = groupBy(groupByGroup[0], function(item)
  {
    return [item.category,item.group];
  })

Then, remove the condition if(o.group=="x") from the groupBy function. This issue can be resolved easily with underscore.js. For assistance, refer to this question:

Underscore.js: Sum of items in a collection

I hope this explanation proves helpful to you.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Increased wait time during initial execution

Currently facing an issue with delaying the first run of a function. I've developed a basic slideshow that is causing problems due to this delay in the initial run. My goal is to have the first run wait for 10 seconds and then maintain a 4-second del ...

The JavaScript function is not executing the Node.js code as expected

Currently, I am utilizing Twilio to send a sample message when running the provided code in the terminal: var accountSid = '...'; var authToken = '...'; var twilio = require('twilio'); var client = new twilio(acco ...

Employing an array for transmitting parameters to a function

I am wondering about the structure of my code which includes variables x_1, x_2, x_3, …., x_n being passed in an array. Here is how it looks: var x_1 = "something"; var x_2 = "something"; var x_3 = "something"; . . . var x_n = "something"; var parameter ...

Utilize fetch API in React to streamline API responses by filtering out specific fields

I have received an API response with various fields, but I only need to extract the description and placeLocation. results: [{placeId: "BHLLC", placeLocation: "BUFR", locationType: "BUFR",…},…] 0: {placeId: "BHLL ...

Upon clicking, the Bootstrap dropdown button fails to open up

Recently while working on my Ruby on Rails project, I encountered an issue with implementing a dropdown button similar to the one on Bootstrap's site. Unfortunately, the button isn't functioning as expected and is throwing an error in the browser ...

Sharing resources between different origins and the file:// protocol

I have been developing an HTML5 application that is collecting data from various sources using JSONP. So far, everything involving a GET request has been functioning perfectly. However, I have encountered a hurdle while attempting to make a POST request. T ...

The number of child notes can vary depending on the browser being used

Having an issue with a JavaScript function that is supposed to read an XML file. function readXML() { alert("readXML"); if(xmlDoc.readyState == 4 || xmlDoc.readyState == 'complete') { for(var i=0; i < xmlDoc.getElementsByT ...

Having trouble getting the onClick event to trigger in React?

I have a button in my navbar that triggers a submenu (list of items) to display when clicked. Each item is a separate child component and I want them to trigger an event when clicked. However, the onClick event listener does not seem to be working. Other m ...

Leveraging JSON data with jQuery's ajax function

Utilizing jQuery to retrieve data from this API () has been a success. I have successfully fetched the main details such as "name", "height", and "mass". However, I am facing a challenge when trying to access the values of "homeworld", "films", "species", ...

Is the Flowplayer JS API malfunctioning due to Flowplayer not being properly 'loaded'?

On a webpage, I have implemented multiple videos to autoplay using Flowplayer and jQuery with the following code: $('.video').each(function (k, obj) { $(obj).flowplayer(...) }) These videos are streaming and start playing automatically. At a ...

Spin an object on a stationary axis

https://i.sstatic.net/8dT9W.gif Is there a method to create a similar effect using CSS, JS, or GSAP? ...

Learn how to properly display the state array within a React component. Even though the array is present in the state, you may encounter issues where it

I am facing an issue while trying to display data from firestore in my React component. I have updated the global state array with the firestore data and it is being updated, but when I try to render that array, it shows as undefined. Initially, I attempt ...

Highcharts: Including plotlines in the legend

I need to include a plotline in the legend. Check out my example here. $('#container').highcharts({ xAxis: { tickInterval: 24 * 3600 * 1000, // one day type: 'datetime' }, yAxis: { plotLines: ...

Avoiding mocking a specific module with jest forever

Our codebase heavily relies on underscore in various parts, and I prefer to avoid mocking it. I'm aware that I can use jest.unmock('underscore'); in each test that utilizes underscore. Is there a method to unmock underscore across all tests ...

Utilize SWR to retrieve data that corresponds to the chosen value from the dropdown menu

Can SWR fetch data based on the selected value in a dropdown? Initially, it works, but when I select a previously selected value, it doesn't fetch the correct data. Using the Fetch API const { data: entities } = useSWR( currentEntity?.enti ...

What is the best way to transfer XML data format from a web browser to a server using jQuery?

Is there a method in jQuery to transmit data to the server from a browser in XML format? Thank you, Sana. ...

Merging two arrays that have identical structures

I am working on a new feature that involves extracting blacklist terms from a JSON file using a service. @Injectable() export class BlacklistService { private readonly BLACKLIST_FOLDER = './assets/data/web-blacklist'; private readonly blackl ...

Submitting modal form information using AJAX to PHP

As a novice in the realm of web programming, I find myself seeking some guidance to untangle a riddle. Regrettably, my grasp of the basics still leaves much to be desired. Within my main page, view.adminsettings.php, I've designated a Navigation DIV ...

Displaying individual attributes of objects through v-for loop

I have created a table-making component to streamline the process of creating multiple tables. Each table consists of three key elements: The object (an array of objects with one row per object) Headers specific to each table The object properties that n ...

The onClick event is not functioning properly with React's select and option elements

Looking for a way to get the option value from each region? const region = ['Africa','America','Asia','Europe','Oceania']; <div className="options"> <select> ...