I'm using the Aggregates feature of a Kendo UI Grid Widget to display the sum of a grouped column. The column in question is bound to an optional field / property so when the data set includes a data item where property is not present (i.e. undefined), the sum ends up being NaN as you would expect.
The Kendo DataSource calculates these aggregates "when the data source populates with data" but does not include a feature to allow custom aggregates that would allow me to implement a version of sum that substitutes 0 for undefined values.
I can see where the sum aggregate function is defined in kendo.data.js but I would prefer not to change that if possible.
I have an idea how to solve this by writing a function to query the $("#myGridId").data("kendoGrid").dataSource but I'd like to know if there is a better option.
After comparing the latest code in kendo.data.js to my project version (2013.1.319) I see that they have changed the implementation of the sum aggregate function to handle this case by only performing the addition if the value is a number. Problem solved if I can get the project updated to the latest version of Kendo UI.
The code snippet below is at line 1369 of version 2014.1.416.
sum: function(accumulator, item, accessor) {
var value = accessor.get(item);
if (!isNumber(accumulator)) {
accumulator = value;
} else if (isNumber(value)) {
accumulator += value;
}
return accumulator;
}
Related
I have the below requirement to be implemented in a plugin code on an Entity say 'Entity A'-
Below is the data in 'Entity A'
Record 1 with field values
Price = 100
Quantity = 4
Record 2 with field values
Price = 200
Quantity = 2
I need to do 2 things
Add the values of the fields and update it in a new record
Store the Addition Formula in a different config entity
Example shown below -
Record 3
Price
Price Value = 300
Formula Value = 100 + 200
Quantity
Quantity Value = 6
Formula Value = 4 + 2
Entity A has a button named "Perform Addition" and once clicked this will trigger the plugin code.
Below is the code that i have tried -
AttributeList is the list of fields i need to perform sum on. All fields are decimal
Entity EntityA = new EntityA();
EntityA.Id = new Guid({"Guid String"});
var sourceEntityDataList = service.RetrieveMultiple(new FetchExpression(fetchXml)).Entities;
foreach (var value in AttributeList)
{
EntityA[value]= sourceEntityDataList.Sum(e => e.Contains(value) ? e.GetAttributeValue<Decimal>(value) : 0);
}
service.Update(EntityA);
I would like to know if there is a way through linq I can store the formula without looping?
and if not how can I achieve this?
Any help would be appreciated.
Here are some thoughts:
It's interesting that you're calculating values from multiple records and populating the result onto a sibling record rather than a parent record. This is different than a typical "rollup" calculation.
Dynamics uses the SQL sequential GUID generator to generate its ids. If you're generating GUIDs outside of Dynamics, you might want to look into leveraging the same logic.
Here's an example of how you might refactor your code with LINQ:
var target = new Entity("entitya", new Guid("guid"));
var entities = service.RetrieveMultiple(new FetchExpression(fetchXml)).Entities.ToList();
attributes.ForEach(a => target[a] = entities.Sum(e => e.GetAttributeValue<Decimal>(a));
service.Update(target);
The GetAttributeValue<Decimal>() method defaults to 0, so we can skip the Contains call.
As far as storing the formula on a config entities goes, if you're looking for the capability to store and use any formula, you'll need a full expression parser, along the lines of this calculator example.
Whether you'll be able to do the Reflection required in a sandboxed plugin is another question.
If, however, you have a few set formulas, you can code them all into the plugin and determine which to use at runtime based on the entities' properties and/or config data.
I have a set of unique items (Index) to each of which are associated various elements of another set of items (in this case, dates).
In real life, if a date is associated with an index, an item associated with that index appeared in a file generated on that date. For combination of dates that actually occurs, I want to know which accounts were present.
let
Source = Table.FromRecords({
[Idx = 0, Dates = {#date(2016,1,1), #date(2016,1,2), #date(2016,1,3)}],
[Idx = 1, Dates = {#date(2016,2,1), #date(2016,2,2), #date(2016,2,3)}],
[Idx = 2, Dates = {#date(2016,1,1), #date(2016,1,2), #date(2016,1,3)}]},
type table [Idx = number, Dates = {date}]),
// Group by
Grouped = Table.Group(Source, {"Dates"}, {{"Idx", each List.Combine({[Idx]}), type {number}}}),
// Clicking on the item in the top left corner generates this code:
Navigation = Grouped{[Dates={...}]}[Dates],
// Which returns this error: "Expression.Error: Value was not specified"
// My own code to reference the same value returns {0,2} as expected.
CorrectValue = Grouped{0}[Idx],
// If I re-make the table as below the above error does not occur.
ReMakeTable = Table.FromColumns(Table.ToColumns(Grouped), Table.ColumnNames(Grouped))
in ReMakeTable
It seems that I can use the results of this in my later work even without the Re-make (I just can't preview cells correctly), but I'd like to know if what's going on that causes the error and the odd code at the Navigation step, and why it disappears after the ReMakeTable step.
This happens because when you double click an item, the auto-generated code uses value filter instead of row index that you are using to get the single row from the table. And since you have a list as a value, it should be used instead of {...}. Probably UI isn't capable to work with lists in such a situation, and it inserts {...}, and this is indeed an incorrect value.
Thus, this line of code should look like:
Navigate = Grouped{[Dates = {#date(2016,1,1), #date(2016,1,2), #date(2016,1,3)}]}[Idx],
Then it will use value filter.
This is a bug in the UI. The index the UI calculates is incorrect: it should be 0 instead of [Dates={...}]. ... is a placeholder value, and it generates the "Value was not specified" exception if it is not replaced.
I have a table with 3 columns and when I sort column 1 or 2 then I would like to sort column 3 by descending.
I'm using fnPreDrawCallback (I tried to use fnDrawCallback too).
Here is my code:
"fnPreDrawCallback": function (oSettings) {
var column = oSettings.aaSorting[0][0];
if (column !== 3) {
oSettings.aaSorting.push([3, "desc"]);
}
}
This code looks fine, but, for example, when I sort column 1, the column 3 isn't sorted and, more awkward, when I try to sort at a 2nd time on column 1, the sort result is always ascending.
How can I achieve this?
The data has already been "sorted" by the time the preDrawCallback is fired, so changing aaSorting in here won't change the outcome of the table.
You can verify by checking oSettings. There is aiDisplay and aiDisplayMaster properties. aiDisplay is an ordered array of the elements that changes as they are sorted from clicks on a th. aiDisplayMaster is the original order that the data came back from the server or was existing on the page.
I would recommend manually calling fnSort from the click event of your th elements. To do this you will have to unbind the click event that datatables installs.
Create a function like this and call it from fnInitComplete so it only runs after datatables has been setup.
function unbindDTSorting() {
//unbind sort event, prevent sorting when header is clicked
$j('[id$=table] th').unbind('click.DT');
//create your own click handler for the header
$j('[id$=table] th').click(function(e) {
var oTable = $j('[id$=table]').dataTable();
//here is where you can do all of your if/else logic to create the desired multidimensional sorting
if(this.cellIndex == 3){
oTable.fnSort( [ [2, 'asc'],[3,'desc' ]] );
}
});
}
I have a column in grid with type of float and I would like to have numbers displayed without 'e'. Is that possilbe?
e.g.
I don't want -1e-7, but -0.0000001.
What format should I use for that column?
Found a solution. The problem is in Kendo DataSource, which automatically converts this kind of data. The solution is to have column template which would enforce formatting of value.
column.template = function(val) {
return kendo.toString(parseFloat(val[column.name]), '#.#######');
}
My datasource has a column that contains a comma-separated list of numbers.
I want to create a dataset that takes those numbers and turns them into groupings to use in a bar chart.
requirements
numbers will be between 0-17 inclusive
groupings: 0-2,3-5,6-10,11-17
x-axis labels have to be the groupings
y-axis is the percent of rows that contain that grouping
note that because each row can contribute to multiple columns the percentages can add up to > 100%
any help you can offer would be awesome... i'm very new to BIRT and have been stuck on this for a couple days now
Not sure that I understand the requirements exactly, but your basic question "split dataset column into multiple rows" can be solved either using a scripted dataset or with pure SQL (depending on your DB).
Either way, you will need a second dataset (e.g. your data model is master-detail, and in your layout you will need something like
Table/List "Master bound to master DS
Table/List "Detail" bound to detail DS
The detail DS need the comma-separated result column from the master DS as an input parameter of type "String".
Doing this with a scripted dataset is quite easy IFF you understand Javascript AND you understand how scripted datasets work: Create a report variable "myValues" of type object with a default value of null and a second report variable "myValuesIndex" of type integer with a default value of 0.
(Note: this is all untested!)
Create the dataset "detail" as a scripted DS, with one input parameter "csv" of type String and one output parameter "value" of type String.
In the open event of the scripted DS, code:
vars["myValues"] = this.getInputParameterValue("csv").split(",");
vars["myValuesIndex"] = 0;
In the fetch event, code:
var i = vars["myValuesIndex"];
var len = vars["myValues"].length;
if (i < len) {
row["value"] = vars["myValues"][i];
vars["myValuesIndex"] = i+1;
return true;
} else {
return false;
}
For example, for the master DS result row with csv = "1,2,3-4,foo", the detail DS will result in 4 rows with
value = "1"
value = "2"
value = "3-4"
value = "foo"
Using an Oracle DB, this can be done without Javascript. The detail DS (with the same input parameter as above) would then look like:
select t.value as value from table(split(?)) t
For the definition of the split function, see RedFilter's answer on
Is there a function to split a string in PL/SQL?
If you get ORA-22813, you should change the original definition
create or replace type split_tbl as table of varchar2(32767);
to
create or replace type split_tbl as table of varchar2(4000);
as mentioned on https://community.oracle.com/thread/2288603?tstart=0
It's also possible with pure SQL in 11g using regexp_substr (see the same page).
create parameters in the scripted data set. we have to pass or link actual dataset values to scripted dataset parameters through DataSet parameter Binding after assigning the scripted data set to Table.