I'm working with jqGrid and have hit an issue that I can't quite identify...
I've got a grid setup with 2 level multi-grouping, and I've also got a select list to dynamically change the grouping. This all works well so far, and I can change the grouping appropriately based on the selected option. For example:
Project 1
File 1
field 1, field 2, field 3
field 1, field 2, field 3
field 1, field 2, field 3
File 2
field 1, field 2, field 3
field 1, field 2, field 3
field 1, field 2, field 3
File 3
field 1, field 2, field 3
field 1, field 2, field 3
field 1, field 2, field 3
Project 2
File 8
field 1, field 2, field 3
File 9
field 1, field 2, field 3
In this illustration, the project number is the output group and the file number is the inner group. Under each file group is a list of all of the rows in that group.
The issue is that I need an option to remove the inner grouping only and keep the outer grouping intact. I've tried the below code, but it doesn't appear to be doing anything:
if (val == "clear")
{
failingRulesGrid.jqGrid('setGridParam',
{
groupingView:
{
groupField: ['ename'],
groupOrder: ['asc']
}
}).trigger('reloadGrid');
}
This is the code for dynamically changing the multi-grouping, and it is working perfectly:
if (val != "clear")
{
failingRulesGrid.jqGrid('setGridParam',
{
groupingView:
{
groupField: ['ename', val],
groupColumnShow: [false, false],
groupOrder: ['asc', 'asc']
}
}).trigger('reloadGrid');
}
Does anyone have any thoughts on how I can remove the inner group but retain the output group? As a side note, the outer group will only ever be based on the 'ename' column so I don't need to worry about changing how the outer group is constructed.
Not sure why I didn't think of this before, but I've got a working solution. Using 'groupingGroupBy' solves this pretty easily:
if (val == "clear")
{
gridSelector.jqGrid('groupingGroupBy', 'ename',
{
groupColumnShow: [false],
groupCollapse: true
});
}
Related
In a sheet I have two rows that I'd like to sort on:
ColA: Archived - True/False checkbox
ColB: SortOrder - number, a way I have at the moment to group things
Cols C-G: various details
I have a function that does this from a macro, as I'll add a button to the sheet later to run this:
function SortServices() {
var ss = SpreadsheetApp.getActive();
ss.getRange('A1:B').activate();
ss.getActiveRange().offset(1, 0, spreadsheet.getActiveRange().getNumRows() - 1).sort([{column: 1, ascending: true}, {column: 2, ascending: true}]);
};
I'm having a problem, as there are 1000 rows but only ~300 rows currently have data. Of these, about 200 are unchecked and 100 checked (as Archived). So this sorts the entire colA, then colB, which doesn't correspond to the data cols.
Struggling to explain this, imgs might be better. Here's an example, originally:
After applying the Sort:
The issue must be the rows without data, but ColA has checkboxes, which by default are unchecked. Is there a simple way to get around this? (I don't know much about Sheets)
[Edit] Sorry, should have added what I would like... the rows with no data at the end, same as the first img. When a row is checked as Archived, using Sort would then move it down into the lower section together with other Archived items:
Solution
Divide the data in two sets and sort in the desired order those that meet the requirements (have data in column B).
Code
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var lr = ss.getLastRow()
// original values
var range = ss.getRange(1, 1, lr, 3).getValues()
// cells without data in colB
var filtered = range.filter(function (value, index, arr) {
return value[1] == "";
});
// cells with data in colB
var original = range.filter(x => !filtered.includes(x));
// write
ss.getRange(1, 1, original.length, 3).setValues(original).sort([{ column: 1, ascending: true }, { column: 2, ascending: true }]);
ss.getRange(1 + original.length, 1, filtered.length, 3).setValues(filtered)
}
Thought there would be an easier answer, but this works OK:
Inserted a new colA with formula to create a single sortable column:
=if( isblank(D2), 20000, if(B2=true, 10000+C2, C2 ) )
Then used this script:
function SortServices() {
var ss = SpreadsheetApp.getActive();
var sheet = SpreadsheetApp.getActive().getActiveSheet();
sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 1, ascending: true});
};
Probably add this to a custom menu.
Summary
I want to display a bar chart whose dimension is days and is stacked by a different category (i.e. x-axis = days and stack = category-1). I can do this "manually" in that I can write if-then's to zero or display the quantity, but I'm wondering if there's a systematic way to do this.
JSFiddle https://jsfiddle.net/wostoj/rum53tn2/
Details
I have data with dates, quantities, and other classifiers. For the purpose of this question I can simplify it to this:
data = [
{day: 1, cat: 'a', quantity: 25},
{day: 1, cat: 'b', quantity: 15},
{day: 1, cat: 'b', quantity: 10},
{day: 2, cat: 'a', quantity: 90},
{day: 2, cat: 'a', quantity: 45},
{day: 2, cat: 'b', quantity: 15},
]
I can set up a bar chart, by day, that shows total units and I can manually add the stacks for 'a' and 'b' as follows.
var dayDim = xf.dimension(_ => _.day);
var bar = dc.barChart("#chart");
bar
.dimension(dayDim)
.group(dayDim.group().reduceSum(
_ => _.cat === 'a' ? _.quantity : 0
))
.stack(dayDim.group().reduceSum(
_ => _.cat === 'b' ? _.quantity : 0
));
However, this is easy when my data has only 2 categories, but I'm wondering how I'd scale this to 10 or an unknown number of categories. I'd imagine the pseudo-code I'm trying to do is something like
dc.barChart("#chart")
.dimension(xf.dimension(_ => _.day))
.stackDim(xf.dimension(_ => _.cat))
.stackGroup(xf.dimension(_ => _.cat).group().reduceSum(_ => _.quantity));
I mentioned this in my answer to your other question, but why not expand on it a little bit here.
In the dc.js FAQ there is a standard pattern for custom reductions to reduce more than one value at once.
Say that you have a field named type which determines which type of value is in the row, and the value is in a field named value (in your case these are cat and quantity). Then
var group = dimension.group().reduce(
function(p, v) { // add
p[v.type] = (p[v.type] || 0) + v.value;
return p;
},
function(p, v) { // remove
p[v.type] -= v.value;
return p;
},
function() { // initial
return {};
});
will reduce all the rows for each bin to an object where the keys are the types and the values are the sum of values with that type.
The way this works is that when crossfilter encounters a new key, it first uses the "initial" function to produce a new value. Here that value is an empty object.
Then for each row it encounters which falls into the bin labelled with that key, it calls the "add" function. p is the previous value of the bin, and v is the current row. Since we started with a blank object, we have to make sure we initialize each value; (p[v.type] || 0) will make sure that we start from 0 instead of undefined, because undefined + 1 is NaN and we hate NaNs.
We don't have to be as careful in the "remove" function, because the only way a row will be removed from a bin is if it was once added to it, so there must be a number in p[v.type].
Now that each bin contains an object with all the reduced values, the stack mixin has helpful extra parameters for .group() and .stack() which allow us to specify the name of the group/stack, and the accessor.
For example, if we want to pull items a and b from the objects for our stacks, we can use:
.group(group, 'a', kv => kv.value.a)
.stack(group, 'b', kv => kv.value.b)
It's not as convenient as it could be, but you can use these techniques to add stacks to a chart programmatically (see source).
I am having some problem for a custom sorting required for one of row groups that I have in a SSRS table.
The logic for the custom sort order -
If the row group value contains a particular value then it should always be displayed at the bottom and all the other values have to be displayed in the ascending order.
For e.g. -
Suppose from the list of values A,E,G,D,C,and F, "D" should be always be displayed last and the other elements are to be sorted in asc order.
So, the above list should be sorted in the following order -
A,B,C,E,F,G,D
Or if the list of elements is - P,J,M,N,D,C,K
the required sort order is -
C,J,K,M,N,P and D.
This logic has to be implemented for the row group data which gets displayed in the report.
Would appreciate if someone can help me out on this.
Thank you.
Try using the following expression in the Sorting setting.
=IIF(
Fields!YourField.Value="D","ZZZZ" & Fields!YourField.Value,
Fields!YourField.Value
)
This will sort your group if you don't have groups whose four first letters are ZZZZ.
Let me know if this helps.
I use an IIF (or multiple IIFs) to do custom sorts like this.
For your situation:
A,E,G,D,C,and F, D should be always be displayed last and the other
elements are to be sorted in asc
I would first do a custom sort:
=IIF(Fields!MyFIeld.Value = "D", 2, 1)
This would sort the D first.
Then add second Sort that just uses the field (Myfield) to sort the rest by the field.
For the second situation:
if the list of elements is - P,J,M,N,D,C,K the required sort order is C,J,K,M,N,P and D
Then I would make a single custom sort with multiple IIFs:
=IIF(Fields!MyFIeld.Value = "C", 1,
IIF(Fields!MyFIeld.Value = "J", 2,
IIF(Fields!MyFIeld.Value = "K", 3,
IIF(Fields!MyFIeld.Value = "M", 4,
IIF(Fields!MyFIeld.Value = "N", 5,
IIF(Fields!MyFIeld.Value = "P", 6,
IIF(Fields!MyFIeld.Value = "D", 7, 8)))))))
-
I created a tablix with totals and I was able to sort by alphabetic order, total(ascending), total(descending). First I create a Dataset like this:
Select 'Name' as Order_Col, 1 as Order_Num
union
Select 'Ascending' as Order_Col, 2 as Order_Num
union
Select 'Descending' as Order_Col, 3 as Order_Num
order by 2
Then in the column group section, group properties I insert the following expression in the sorting options:
=Switch(Parameters!SortOrder.Value = 1,Fields!Name.Value
,Parameters!SortOrder.Value = 3,(Fields!TtlRef.Value)*-1
,Parameters!SortOrder.Value = 2,Fields!TtlRef.Value)
Create a Parameter named SortOrder where the Value is Order_Num and Label is Order_Col.
You can make a default using a value of 1.
For columns in y-dimension how to do natural sort for alpha numeric column names ?
For example:
consider column names AA1, AA2, AA3, AA10, AA11.
These are listed in order AA1, AA10, AA11, AA2, AA3 in pivot table y-dimension.
Desired order of columns is AA1, AA2, AA3, AA10, AA11
Free jqGrid 4.9 contains full rewritten version of jqPivot. I tried to hold compatibility with the previous version, but it contains many advanced features. I tried to describe there in wiki.
Not so many people uses jqPivot. So I remind what it do. It gets an input data as source and generate new data, which will be input data for jqGrid. Additionally jqPivot generates colModel based on input data and yDimension parameter. During analyzing of input data jqPivot sorts input data by xDimension and by yDimension. The order or sorting of xDimension defines the order of rows of resulting grid. The order or sorting of yDimension defines the order of columns of resulting grid and the total number of resulting columns. The options compareVectorsByX and compareVectorsByY of allows to specify callback function which will be used for custom sorting by the whole x or y vector. It's important to understand that sorting function not only specify the the order of columns, but it informs jqPivot which vectors should be interpreted as the same. For example it can interpret the values 12, 12.0 and 012.00 as the same and specify that 12.0 is larger as 6.
I describe below some ways which can be used to customize sorting by xDimension and yDimension.
First of all one can specify skipSortByX: true or skipSortByY: true parameters. In the case the input data have to be already sorted in the order which you want. The next important options are Boolean options caseSensitive (with default value false) and trimByCollect (default value true). caseSensitive: true can be used to distinguish input data by case and trimByCollect: false can be used to hold trailing spaces in the input data.
Some other important option can be specified in xDimension or yDimension: sorttype and sortorder. sortorder: "desc" can be used to reverse the order of sorted data. The option sorttype can be "integer" (or "int") which means to truncate (Math.floor(Number(inputValue))) input data during sorting; The values "number", "currency" and "float" means that the input data should be converted to numbers during sorting (Number(inputValue)). Finally one can don't specify any sorttype, but specify compare callback function instead. The compare callback is function with two parameters and it should return well known -1, 0 or 1 values.
For example I created the demo for one issue. One asked my about the following situation. The web site contains login, which identifies the country of the user. One want to set the user's country as the first in the sorting order. The demo uses the following yDimension parameter:
yDimension: [
{ dataName: "sellyear", sorttype: "integer" },
{ dataName: "sell month",
compare: function (a, b) {
if (a === "Germany") { return b !== "Germany" ? -1 : 0; }
if (b === "Germany") { return 1; }
if (a > b) { return 1; }
if (a < b) { return -1; }
return 0;
}}
]
It sets "Germany" first in the sorting order. As the results one sees the results like on the picture below
You can use the same approach using the code for natural compare from the answer and you will implements your requirements.
In more advanced cases one can use the options compareVectorsByX and compareVectorsByY. The requirement was to place specific country only in one specific year on the first place holding the standard order on all other cases. The corresponding demo uses compareVectorsByY to implement the requirement. It displays
and uses the following compareVectorsByY:
compareVectorsByY: function (vector1, vector2) {
var fieldLength = this.fieldLength, iField, compareResult;
if (fieldLength === 2) {
if (vector1[0] === "2011" && vector1[1] === "Germany") {
if (vector2[0] === "2011" && vector2[1] === "Germany") {
return {
index: -1,
result: 0
};
}
return {
index: vector2[0] === "2011" ? 1 : 0,
result: -1
};
}
// any vector1 is larger as vector2 ("2011", "Germany")
if (vector2[0] === "2011" && vector2[1] === "Germany") {
return {
index: vector2[0] === "2011" ? 1 : 0,
result: 1
};
}
}
for (iField = 0; iField < fieldLength; iField++) {
compareResult = this.fieldCompare[iField](vector1[iField], vector2[iField]);
if (compareResult !== 0) {
return {
index: iField,
result: compareResult
};
}
}
return {
index: -1,
result: 0
};
}
It's important to mention that compareVectorsByY callback function should return object with two properties: index and result. The value of result property should be -1, 0 or 1. The value of index property should be -1 in case of result: 0 and be 0-based index of vectors where vector1 and vector2 are different.
I have a sorting question. I have two columns in my table that I need to sort by (primary=column 1, secondary=column 4). I am using the script below and it works for sorting by column 1, and it works for sorting column 4 if I also make a change in Column 1 but not by itself. I would like to be able to edit a value JUST column 4 value and have it do the secondary sort.
Ex: Column 1: Date, Column 2: LastName, Column 3: DOB, Column 4: Payment
I'd like to maintain the spreadsheet in date order (ascending, allowing repeat values), but secondarily organized by payment amount (ascending). That way I can see by date the order of highest to lowest payments. If I change JUST the payment data in a cell, I'd like it to resort by column 1 (date) and then 4 (payment). This script is not accomplishing that last bit. Any advice?
/**
* Automatically sorts the 1st column (not the header row) Ascending.
*/
function onEdit(event){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 1;
var tableRange = "A2:T99"; // What to sort.
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( [1, 4] ); // or
range.sort( [{ column: 1, ascending: true }, 4] ); // or
range.sort( [{ column: 1, ascending: true }, { column: 4, ascending: true }] );
}
}
Probably I don't understand the problem? I suggest to change your if statement from
if(editedCell.getColumn() == columnToSortBy){
...
}
to
if((editedCell.getColumn() == columnToSortBy) || (editedCell.getColumn() == 4)){
...
}