How to draw an aggregated table with dc.js? - dc.js

I've been bugged by a probably trivial issue, but I want to draw two tables grouped by dimension.
eg:
var data=[{country:"US", medal:"gold",sport:"jump",year:"2015"},
{country:"US", medal:"silver",sport:"jump",year:"2011"}
{country:"CN", medal:"gold",sport:"jump",year:"2015"}}
and would like to draw a few tables:
country medals
US 2
CN 1
and
medal qty
gold 2
silver 1
so a trivial dim + group on crossfilter, but as dataTable doesn't take "real" groups as groups... bit stuck
Any suggestion?

Generally speaking, you can use a group as the dimension for dc.dataTable if you just give it a bottom method. You can do something like this:
var cf = crossfilter(["a","a","b","b","c","d","d","d"])
var dim = cf.dimension(function(d) { return d; });
var grp = dim.group()
grp.bottom = grp.top;
var dataTable = dc.dataTable('#dataTable');
dataTable
.dimension(grp)
.group(function() { return ""; })
.size(100)
.columns(['key', 'value']);
dc.renderAll();
The problem is that you need to put all the necessary information into the group. You can either do this in a custom group, or you can create a fake group wrapping your group where you look up additional information and add it to the group when the dataTable queries the fake group.

Related

DC.js - Select Menu as a filter for Composite charts

My dataset is similar to this.
name,code,proc,subP,date,m,a,b,o,t
BW,1333,29,1,2015-12-29 02:30:00,10,0,0,0,10
BW,1333,29,1,2015-12-29 12:00:00,25,0,0,0,25
BW,1333,29,1,2015-12-30 12:00:00,26,0,0,0,26
BW,1333,29,2,2015-12-31 12:00:00,27,0,0,0,27
BW,1333,29,2,2016-01-01 12:00:00,26.1,4.9,1.8,0,32.8
BW,1333,29,2,2016-01-02 12:00:00,26.4,4.9,1.9,0,33.2
BW,1333,29,2,2016-01-03 12:00:00,26.2,4.9,1.9,0,33
...
NS,1212,11,1,2016-07-28 15:30:00,1.6,3.7,4.4,0,9.7
NS,1212,11,1,2016-07-29 12:00:00,17.4,2.3,0,0,19.7
NS,1212,11,1,2016-07-30 12:00:00,21,5,14.1,0,40.1
NS,1212,11,2,2016-07-31 11:12:00,18.1,3.5,6.1,0,27.7
NS,1212,11,2,2016-07-31 12:00:00,0.1,0.2,0.2,0,0.5
NS,1212,11,2,2016-08-01 12:00:00,0.1,2.7,2.6,0,5.4
I'm using a composite line chart to represent this data.
I'm using selectMenu to filter 'BW' and 'NS' records. But the composite chart remains unchanged when I use selectmenu.
I split the data into two seperate files (one with 'BW' records and the other with 'NS' records) to try to implement the data selection functionality provided in dc.js Series Chart example . That didnt work either.
Here is a fiddle with one of the charts.
Ultimately, I would like to filter multiple charts(composite, series and bubble) by 'BW' and 'NS' records with or without a select menu.
How can I implement this?
The problem isn't with your selectMenu or your composite chart, it's with the way you are reducing data.
The first parameter to group.reduce() is the add function, the second parameter is the remove function, and the final one is the initialize function.
You usually don't want to set p directly from v as you are doing:
var grp = dim.group().reduce(
function(p,v){
p.m = +v.m;
p.a = +v.a;
p.b = +v.b;
return p;
},
function(p,v){
p.m = +v.m;
p.a = +v.a;
p.b = +v.b;
return p;
},
function(){return {m:0, a:0, b:0}; });
That will only work if every key is perfectly unique and no two items ever fall in the same bin.
And you almost never want to set p from v in the remove function. That will prevent anything from ever being removed.
Instead, add and subtract the values like so:
var grp = dim.group().reduce(
function(p,v){
p.m = p.m + +v.m;
p.a = p.a + +v.a;
p.b = p.b + +v.b;
return p;
},
function(p,v){
p.m = p.m - +v.m;
p.a = p.a - +v.a;
p.b = p.b - +v.b;
return p;
},
function(){return {m:0, a:0, b:0}; });
Here's a working fork of your fiddle.

Limiting the results of drop-down in Google Sheets

I work as an operations manager at at small business and I'm trying to set up an order sheet that is easy for the salesman to use. In the order sheet, I've used the OFFSET function to refer to a master list containing customers and prices. Under Customer, I type the customer and it draws it from the master list via an auto-complete drop-down. The same happens with the product.
Here is the order sheet:
Order Sheet Example
My issue is I'll begin typing in the product e.g. 'prawn'. We have over a dozen prawn lines, but a particular customer will only take one. All of the other prawn results have no prices for that customer. However, the auto-complete function will offer up all the 'prawn' results.
In the Master list, I've entered prices for only the products that the particular customer uses. Take a look at what the Master List looks like:
Master List look
Without an excellent memory of what customer wants what, it's an exercise in trial and error. In the above example, I could type 'topside' and if I select the wrong one, no price comes up.
This is frustrating.
I was hoping for a way to limit the auto-complete so that when I type 'prawn' or 'topside' for that customer, it only comes up with auto-complete fields that have the price in it. Can anyone help? Or does anyone know of any work-arounds? I'd be really thankful, the current order system is quite difficult.
I believe this will do what you want. It is a little difficult to tell from your sample data. It uses google apps script and sheet names and/or columms may need to be changed in the script for your data. I am attaching a sample spreadsheet you can copy then try. You will have to approve the script in the copy you make.
function onEdit(e) {
var cust=e.value //The value of the edited cell
var sh=e.source.getActiveSheet().getSheetName()//Name of the active sheet.
var col=e.range.getColumn()//The edited column
var r=e.range.getRow()//The edited row
var row=e.range.offset(0,1).getA1Notation()//Cell A1 notation of cell in same row one column to the right.
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet().getSheetName()//Name of active sheet
var s1=ss.getSheetByName("Sheet1")//Variables for sheets
var s2=ss.getSheetByName("Sheet2")
var s3=ss.getSheetByName("TEMP")
var rng=s2.getDataRange().getValues()//Customer/Products
if(sh=="Sheet1" && col==1){//If sheet1 is active sheet and Customer (column A) is edited.
var array=[]//Array to hold customers products
for (var i=0;i<rng.length;i++){
if(rng[i][0]==cust ){
for(var j=1;j<rng[0].length-1;j++){
if(rng[i][j]!="" ){//If customers product has $ entry add to Array.
array.push([rng[0][j]])
}}}}
s3.clearContents()//Clear old product list from TEMP.
s1.getRange(row).clearContent()//Clear product dropdown
s3.getRange(1,1,array.length,1).setValues(array)//Set new customer product list in TEMP.
drop(row,cust)// Call drop function to build new dropdown.
}
if(sh=="Sheet1" && col==2){//If sheet1 is active sheet and Product (column B) is edited.
var cust1=e.range.offset(0,-1).getValue()//Get customer in A
var prod=e.range.getValue()//Get selected product
for (var i=0;i<rng.length;i++){//Get the customer/product price
if(rng[i][0]==cust1 ){
for(var j=1;j<rng[0].length-1;j++){
if(rng[0][j]==prod){
price=rng[i][j]
s1.getRange(r,5).setValue(price)//Set the price in column E
s1.getRange(r,2).clearDataValidations() //Remove the data validation dropdown in column B
}
}}
}}}
function drop(row,cust){
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s3=ss.getSheetByName("TEMP")
var s1=ss.getSheetByName("Sheet1")
var cell = s1.getRange(row);//set validation in B
var ocell=s1.getRange(row).offset(0, -1).getValue()//evaluate value in A
var cellVal=cell.getValue()
if(ocell==cust){
var lr= ss.getSheetByName("TEMP").getLastRow()
var range = ss.getSheetByName("TEMP").getRange(1, 1, lr, 1)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(range).build();//Build the dropdown
cell.setDataValidation(rule)}//Set the validation rules (Customers
products)
}
Test spreadsheet:
https://docs.google.com/spreadsheets/d/1u86sdf1_mO-Mv7hQM_hRZl3Gma-Y2lsu9ZvxSW4jA1U/edit?usp=sharing

Is it possible to use a conditional validation rule as a custom formula in validation box in Google Spreadsheet?

I would like to set a validation in Column D (please see the published spreadsheet at the link below [1]) in such a way that it only shows the items of either column A (A3:A) or B (B3:B) (but not all the items of both columns together) depending upon the Service which is put in Col. C.
So, if somebody put "Typing Services" in C3, the cell D3 has a validation having all the items of column A (A3:A) only. And similarly, if somebody put "Translation Services" in C3, the cell D3 has a validation having all the items of column B (B3:B) only.
I believe a custom formula in validation box in column D can do it. However, i am unable to produce it.
Thank you for your solution!
[1]https://docs.google.com/spreadsheets/d/1XdcKbxKvIOSFK37zwULZns6giE9ounHPS5iHrRFkvIk/edit#gid=697587372 [1]
This should do what you want. Add this to your script editor:
function onEdit(event){
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getSheetByName("custom formula in validation")
var sheet = event.source.getActiveSheet().getName();
var lr=event.source.getActiveSheet().getLastRow()
var editedCell = event.range.getSheet().getActiveCell();
var editVal=event.range.getSheet().getActiveCell().getValue()
var editRow=event.range.getSheet().getActiveCell().getRow()
if(sheet=="custom formula in validation" && editedCell.getColumn() ==3){
if(editVal=="Typing Services"){
var validation=s.getRange(3, 1, lr,1)
var cell= s.getRange(editRow,4,1,1)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(validation).build();
cell.setDataValidation(rule);
}
if(editVal=="Translation Services"){
var validation=s.getRange(3, 2, lr,1)
var cell= s.getRange(editRow,4,1,1)
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(validation).build();
cell.setDataValidation(rule);
}}}
This is a share of my test spreadsheet:
https://docs.google.com/spreadsheets/d/10lQnZ3m6wJYAC29ylZEM1Yj2vCzKljcE9zGZNn-bRiQ/edit?usp=sharing

Adding multiple selected values from an array to a Kendo Multi Select

Background:
I getting values from a db that will be needed to be pre-selected in a kendo multi select. I am able to do this with one word, however when the return value from the DB is multiple words I run into problems.
Problem:
I am not able to populate pre-selected values in my kendo multi select. In the best cases when I only get one returned word I am able to run these two lines var value = multiSelect.value(); multiSelect.value(["test"]); and the multi select would be populated with the test selection. However when I do multiple values from an array, it does not work the same way.
Code:
var keyWordPool = [{Words: "Test"},{Words: "Test2"}, {Words: "Test3"},
{Words: "Test4"},{Words: "Test5"}];
var returnedWords = ["Test","Test4", "Test5"]; **<< This does not work**
var returnedWords = ["Test"]; **<< This does work**
CreateandPopulateMultiSelect(keyWordPool, returnedWords)
function CreateandPopulateMultiSelect(dataSource, wordsToPopulate)
{
var multiSelect = $(".PanelMultiSelect").kendoMultiSelect({
dataSource: dataSource,
filter: "contains",
dataTextField: "Words",
dataValueField: "Words",
select: function (e) {
var item = e.item;
var text = item.text();
var stop = 0;
}
}).data("kendoMultiSelect");
var value = multiSelect.value();
multiSelect.value([wordsToPopulate]);
}
Objective:
I am not able to control the amount of words that come back from the DB, so I will need to be able to add multiple words at any given time, as well as one word. I will need to have wordsToPopulate to already be selected when a person opens a panel bar.
Well, I can't tell you exactly what is happening internally in kendo, but your wordsToPopulate variable is already an array when you pass it in to CreateandPopulateMultiSelect(). If you change
multiSelect.value([wordsToPopulate]);
to
multiSelect.value(wordsToPopulate);
it should work.
http://dojo.telerik.com/#Stephen/aMEma

dc.js - Creating a row chart from multiple columns and enabling filtering

This is similar to dc.js - how to create a row chart from multiple columns but I want to take it a step further and enable filtering when the rows are clicked.
To answer the question "What is it supposed to filter?" - Only show records with value > 0. For example when Row 'a' is clicked it will only show records with value for a > 0. Hence, the Type pie chart will change to foo:1, bar:2
I guess I have to overwrite onClick method? But I am not sure how.
chart.onClick = function(d) {}
jsfiddle from the answer to the above question - http://jsfiddle.net/gordonwoodhull/37uET/6/
Any suggestions?
Thanks!
Okay, here's a solution where if a record has values > 0 for any of the selected rows, that record is included. As #Ethan said, it's a matter of defining a filter handler:
sidewaysRow.filterHandler(function(dim, filters) {
if(filters && filters.length)
dim.filterFunction(function(r) {
return filters.some(function(c) {
return r[c] > 0;
});
})
else dim.filterAll();
return filters;
});
Also, since the filterFunction only has access to the key, we pass the entire record through as the key. This doesn't make a whole lot of sense in the "real world" but since we're already using crossfilter sideways, it is probably fine:
var dim = ndx.dimension(function(r) { return r; });
New version of the fiddle: https://jsfiddle.net/gordonwoodhull/b7cak6xj/
BTW it sounds like you want to only select one row at a time. Here's how to do that:
sidewaysRow.addFilterHandler(function(filters, filter) {
filters.length = 0;
filters[0] = filter;
return filters;
})
(This will be simpler in dc 2.1 on the develop branch, where the charts use the result of the filter handlers instead of requiring you to modify the filters in place; the body becomes just return [filter];)

Resources