I'm trying to combine a jump line with a multiseries column chart. Is it possible to create a jump line specifically for each column of the multiseries column chart? For now the only thing that i could obtain is a jump line for each group of the dataset, here's an example:
anychart.onDocumentReady(function () {
var data = [
["January", 12000, 10000, 8100],
["February", 15000, 12000, 8200],
["March", 16000, 18000, 8300],
];
var dataSet = anychart.data.set(data);
var mapping1 = dataSet.mapAs({x: 0, value: 1});
var mapping2 = dataSet.mapAs({x: 0, value: 2});
var jump1 = dataSet.mapAs({x: 0, value: 3});
// create a chart
var chart = anychart.column();
// create the series and set the data
var series1 = chart.column(mapping1);
var series2 = chart.column(mapping2);
var series3 = chart.jumpLine(jump1);
series3.stroke("3 #FFFF00");
chart.container("container");
chart.draw();
});
If I got your idea correctly, then you can create an additional xScale and xAxis. Then bind the jump series to that additional scale. Here is the live sample based on your code.
If someone needs this, thanks to #AnyChart Support 's answer here's the code for combining this two chart.
anychart.onDocumentReady(function () {
var data = [
["January", 12000, 10000],
["February", 15000, 12000],
["March", 16000, 18000],
];
var jumpData = [
['January_1', 1000],
['January_2', 3000],
['February_1', 5000],
['February_2', 6000],
['March_1', 5000],
['March_2', 7500],
];
var dataSet = anychart.data.set(data);
var jumpDataSet = anychart.data.set(jumpData);
var mapping1 = dataSet.mapAs({x: 0, value: 1});
var mapping2 = dataSet.mapAs({x: 0, value: 2});
var jump1 = jumpDataSet.mapAs({x: 0, value: 1});
// create a chart
var chart = anychart.column();
chart.barGroupsPadding(1);
// Set the padding between bars or columns.
chart.barsPadding(1);
// create the series and set the data
var series1 = chart.column(mapping1);
var series2 = chart.column(mapping2);
var additionalXscale = anychart.scales.ordinal();
chart.xAxis(1).scale(additionalXscale).enabled(false);
var series3 = chart.jumpLine(jump1);
series3.pointWidth('60%');
series3.xScale(additionalXscale);
series3.stroke("5 #00FF00");
series3.name = "AAA"
chart.container("container");
chart.draw();
});
The description of the selection.data function includes an example with multiple groups (link) where a two-dimensional array is turned into an HTML table.
In d3.js v3, for lower dimensions, the accessor functions included a third argument which was the index of the parent group's datum:
td.text(function(d,i,j) {
return "Row: " + j;
});
In v4, this j argument has been replaced by the selection's NodeList. How do I access the parent group's datum index now?
Well, sometimes an answer doesn't provide a solution, because the solution may not exist. This seems to be the case.
According to Bostock:
I’ve merged the new bilevel selection implementation into master and also simplified how parents are tracked by using a parallel parents array.
A nice property of this new approach is that selection.data can
evaluate the values function in exactly the same manner as other
selection functions: the values function gets passed {d, i, nodes}
where this is the parent node, d is the parent datum, i is the parent
(group) index, and nodes is the array of parent nodes (one per group).
Also, the parents array can be reused by subselections that do not
regroup the selection, such as selection.select, since the parents
array is immutable.
This change restricts functionality—in the sense that you cannot
access the parent node from within a selection function, nor the
parent data, nor the group index — but I believe this is ultimately A
Good Thing because it encourages simpler code.
(emphasis mine)
Here's the link: https://github.com/d3/d3-selection/issues/47
So, it's not possible to get the index of the parent's group using selection (the parent's group index can be retrieved using selection.data, as this snippet bellow shows).
var testData = [
[
{x: 1, y: 40},
{x: 2, y: 43},
{x: 3, y: 12},
{x: 6, y: 23}
], [
{x: 1, y: 12},
{x: 4, y: 18},
{x: 5, y: 73},
{x: 6, y: 27}
], [
{x: 1, y: 60},
{x: 2, y: 49},
{x: 3, y: 16},
{x: 6, y: 20}
]
];
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 300);
var g = svg.selectAll(".groups")
.data(testData)
.enter()
.append("g");
var rects = g.selectAll("rect")
.data(function(d, i , j) { console.log("Data: " + JSON.stringify(d), "\nIndex: " + JSON.stringify(i), "\nNode: " + JSON.stringify(j)); return d})
.enter()
.append("rect");
<script src="https://d3js.org/d3.v4.min.js"></script>
My workaround is somewhat similar to Dinesh Rajan's, assuming the parent index is needed for attribute someAttr of g.nestedElt:
v3:
svg.selectAll(".someClass")
.data(nestedData)
.enter()
.append("g")
.attr("class", "someClass")
.selectAll(".nestedElt")
.data(Object)
.enter()
.append("g")
.attr("class", "nestedElt")
.attr("someAttr", function(d, i, j) {
});
v4:
svg.selectAll(".someClass")
.data(nestedData)
.enter()
.append("g")
.attr("class", "someClass")
.attr("data-index", function(d, i) { return i; }) // make parent index available from DOM
.selectAll(".nestedElt")
.data(Object)
.enter()
.append("g")
.attr("class", "nestedElt")
.attr("someAttr", function(d, i) {
var j = +this.parentNode.getAttribute("data-index");
});
I ended up defining an external variable "j" and then increment it whenever "i" is 0
example V3 snippet below.
rowcols.enter().append("rect")
.attr("x", function (d, i, j) { return CalcXPos(d, j); })
.attr("fill", function (d, i, j) { return GetColor(d, j); })
and in V4, code converted as below.
var j = -1;
rowcols.enter().append("rect")
.attr("x", function (d, i) { if (i == 0) { j++ }; return CalcXPos(d, j); })
.attr("fill", function (d, i) { return GetColor(d, j); })
If j is the nodeList...
j[i] is the current node (eg. the td element),
j[i].parentNode is the level-1 parent (eg. the row element),
j[i].parentNode.parentNode is the level-2 parent (eg. the table element),
j[i].parentNode.parentNode.childNodes is the array of level-1 parents (eg. array of row elements) including the original parent.
So the question is, what is the index of the parent (the row) with respect to it's parent (the table)?
We can find this using Array.prototype.indexOf like so...
k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);
You can see in the snippet below that the row is printed in each td cell when k is returned.
var testData = [
[
{x: 1, y: 1},
{x: 1, y: 2},
{x: 1, y: 3},
{x: 1, y: 4}
], [
{x: 2, y: 1},
{x: 2, y: 2},
{x: 2, y: 3},
{x: 2, y: 4}
], [
{x: 3, y: 4},
{x: 3, y: 4},
{x: 3, y: 4},
{x: 3, y: 4}
]
];
var tableData =
d3.select('body').selectAll('table')
.data([testData]);
var tables =
tableData.enter()
.append('table');
var rowData =
tables.selectAll('table')
.data(function(d,i,j){
return d;
});
var rows =
rowData.enter()
.append('tr');
var eleData =
rows.selectAll('tr')
.data(function(d,i,j){
return d;
});
var ele =
eleData.enter()
.append('td')
.text(function(d,i,j){
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);
return k;
});
<script src="https://d3js.org/d3.v4.min.js"></script>
Reservations
This approach is using DOM order as a proxy for data index. In many cases, I think this is a viable band-aid solution if this is no longer possible in D3 (as reported in this answer).
Some extra effort in manipulating the DOM selection to match data might be needed. As an example, filtering j[i].parentNode.parentNode.childNodes for <tr> elements only in order to determine the row -- generally speaking the childNodes array may not match the selection and could contain extra elements/junk.
While this is not a cure-all, I think it should work or could be made to work in most cases, presuming there is some logical connection between DOM and data that can be leveraged which allows you to use DOM child index as a proxy for data index.
Here's an example of how to use the selection.each() method. I don't think it's messy, but it did slow down the render on a large matrix. Note the following code assumes an existing table selection and a call to update().
update(matrix) {
var self = this;
var tr = table.selectAll("tr").data(matrix);
tr.exit().remove();
tr.enter().append("tr");
tr.each(addCells);
function addCells(data, rowIndex) {
var td = d3.select(this).selectAll("td")
.data(function (d) {
return d;
});
td.exit().remove();
td.enter().append("td");
td.attr("class", function (d) {
return d === 0 ? "dead" : "alive";
});
td.on("click", function(d,i){
matrix[rowIndex][i] = d === 1 ? 0 : 1; // rowIndex now available for use in callback.
});
}
setTimeout(function() {
update(getNewMatrix(matrix))
}, 1000);
},
Assume you want to do a nested selectiom, and your
data is some array where each element in turn
contains an array, let's say "values". Then you
have probably some code like this:
var aInnerSelection = oSelection.selectAll(".someClass") //
.data(d.values) //
...
You can replace the array with the values by a new array, where
you cache the indices within the group.
var aInnerSelection = oSelection.selectAll(".someClass") //
.data(function (d, i) {
var aData = d.values.map(function mapValuesToIndexedValues(elem, index) {
return {
outerIndex: i,
innerIndex: index,
datum: elem
};
})
return aData;
}, function (d, i) {
return d.innerIndex;
}) //
...
Assume your outer array looks like this:
[{name "X", values: ["A", "B"]}, {name "y", values: ["C", "D"]}
With the first approach, the nested selection brings you from here
d i
------------------------------------------------------------------
root dummy X {name "X", values: ["A", "B"]} 0
dummy Y {name "Y", values: ["C", "D"]} 1
to here.
d i
------------------------------------------------------------------
root X A "A" 0
B "B" 1
Y C "C" 2
D "D" 3
With the augmented array, you end up here instead:
d i
------------------------------------------------------------------
root X A {datum: "A", outerIndex: 0, innerIndex: 0} 0
B {datum: "B", outerIndex: 0, innerIndex: 1} 1
Y C {datum: "C", outerIndex: 1, innerIndex: 0} 2
D {datum: "D", outerIndex: 1, innerIndex: 1} 3
So you have within the nested selections, in any function(d,i), all
information you need.
Here's a snippet I crafter after re-remembering this usage of .each for nesting, I thought it may be useful to others who end up here. This examples creates two layers of circles, and the parent group index is used to determine the color of the circles - white for the circles in the first layer, and black for the circles in the top layer (only two layers in this case).
const nested = nest().key(layerValue).entries(data);
let layerGroups = g.selectAll('g.layer').data(nested);
layerGroups = layerGroups.enter().append('g').attr('class', 'layer')
.merge(layerGroups);
layerGroups.each(function(layerEntry, j) {
const circles = select(this)
.selectAll('circle').data(layerEntry.values);
circles.enter().append('circle')
.merge(circles)
.attr('cx', d => xScale(xValue(d)))
.attr('cy', d => yScale(yValue(d)))
.attr('r', d => radiusScale(radiusValue(d)))
.attr('fill', j === 0 ? 'white' : 'black'); // <---- Access parent index.
});
My solution was to embed this information in the data provided to d3js
data = [[1,2,3],[4,5,6],[7,8,9]]
flattened_data = data.reduce((acc, v, i) => {
v.forEach((d, j) => {
data_item = { i, j, d };
acc.push(data_item);
});
return acc;
}, []);
Then you can access i, j and d from the data arg of the function
td.text(function(d) {
// Can access i, j and original data here
return "Row: " + d.j;
});
I have bookingData.roomList,
bookingData.roomList = [{id : <>, roomCategory : Single, number : 2}, {id:<>,roomCategory : Double, number : 1}]
And i want to make a new values like roomListData
roomListData = {<uuid> : {roomCateghory : Single, number : 2},
<uuid1> : {roomCateghory : Double, number : 2} }
please some one help me to solve this
I think This is what you need. To use a property value as property label you use []
var roomList = [{id: 4514,roomCategory: "Single", number: 2},
{id: 4542,roomCategory: "Double", number: 1}];
var newJson = [];
roomList.forEach(function(item) {
newJson.push({
[item.id] :{
"roomCategory": item.roomCategory,
"number": item.number
}
});
});
console.log(newJson);
1. using JavaScript for...in loop :
var bookingData = {
"roomList": [{
id: 1,
roomCategory: "Single",
number: 2
}, {
id: 2,
roomCategory: "Double",
number: 1
}]
};
var roomListData = {};
for(var i in bookingData.roomList) {
roomListData[bookingData.roomList[i].id] = {
"roomCategory" : bookingData.roomList[i].roomCategory,
"number" : bookingData.roomList[i].number
}
}
console.log(roomListData);
2. using Array.map() method with ES6 Arrow function :
var bookingData = {
"roomList": [{
id: 1,
roomCategory: "Single",
number: 2
}, {
id: 2,
roomCategory: "Double",
number: 1
}]
};
var roomListData = {};
bookingData.roomList.map(item =>
roomListData[item.id] = {
"roomCategory" : item.roomCategory,
"number" : item.number
});
console.log(roomListData);
3. using ES6 Spread assignment :
var bookingData = {
"roomList": [{
id: 1,
roomCategory: "Single",
number: 2
}, {
id: 2,
roomCategory: "Double",
number: 1
}]
};
function createObj(a,b) {
var roomListData = {};
roomListData[a.id] = a,delete a.id;
roomListData[b.id] = b,delete b.id;
return roomListData;
}
var res = createObj(...bookingData.roomList);
console.log(res);
`
var roomList = [{id : 1, roomCategory : "Single", number : 2}, {id:2,roomCategory : "Double", number : 1}];
var roomListData = {};
for(var i=0;i< roomList.length ;++i){
var temp = roomList[i];
var id = temp.id;
delete temp.id;
roomListData[id]=temp;
}
console.log(roomListData);
`
I assuming by uuid1 you mean a new uuid, not 'uuid' with the number 1 appended.
bookingData.roomList = [{id : <>, roomCategory : Single, number : 2}, {id:<>,roomCategory : Double, number : 1}]
roomList = {};
bookingData.forEach(function(element, index, array) {
var uuid = GenerateNewUUID();
// or uuid = element.id;
delete element.id;
roomList[uuid] = element;
});
How can i add a summary row to a qx.ui.table.Table to display a total for a column.
The only idea yet is to combine two tables, one with the data and one with the totals. Is there any more elegant way how i can handle this?
Edit
The table is used with the qx.ui.table.model.Simpleas table model.
Well, as long as you use qx.ui.table.model.Simple, you can calculate summary row and append it to the data array before passing it to the model. Or you can do it in a listener of the model's dataChanged. Also it's possible to subclass qx.ui.table.rowrenderer.Default to emphasize the row in some way.
Here goes example and here's playground snippet:
qx.Class.define("app.SummaryRowRenderer", {
extend : qx.ui.table.rowrenderer.Default,
members : {
// override
getRowClass : function(rowInfo)
{
var model = rowInfo['table'].getTableModel();
var isSummaryIndex = model.getColumnIndexById('_isSummary');
return rowInfo['rowData'][isSummaryIndex] ? 'summary' : '';
}
},
defer : function()
{
var entry = qx.bom.element.Style.compile({
'backgroundColor' : '#ccc',
'fontWeight' : 'bold'
});
var sheet = qx.bom.Stylesheet.createElement();
qx.bom.Stylesheet.addRule(sheet, '.summary .qooxdoo-table-cell', entry);
}
});
var model = new qx.ui.table.model.Simple();
model.setColumns(
[this.tr('Name'), this.tr('Value'), null],
['name', 'value', '_isSummary']
);
var table = new qx.ui.table.Table(model);
table.set({
'statusBarVisible' : false,
'columnVisibilityButtonVisible' : false,
'showCellFocusIndicator' : false,
'dataRowRenderer' : new app.SummaryRowRenderer()
});
table.getTableColumnModel().setColumnVisible(2, false);
this.getRoot().add(table, {'edge': 0});
var data = [
{'name': 'foo', 'value': 10},
{'name': 'bar', 'value': 100},
{'name': 'baz', 'value': 1000},
{'name': 'quz', 'value': 10000},
];
qx.event.Timer.once(function()
{
var dataWithSummary = qx.lang.Array.clone(data);
dataWithSummary.push(data.reduce(function(r, v)
{
r['value'] += v['value'];
return r;
}, {'name': 'Summary', 'value': 0, '_isSummary': true}));
model.setDataAsMapArray(data);
}, this, 1000);
I create a local object collection based on the user's selection. The dynamic Array should be loaded to the jqGrid. After dynamically creating the array I tried to reload, but nothing happens. Here is the code -
$(document).ready(function () {
var arrobj = [];
var JSONString = []; //[{"DOId":"0","DONo":"Please select","DealerCode":"0","Week":"0","Item":"0","Qty":"11","Date":"11"}]
$("#<%=btnAdd.ClientID%>").click(function () {
//Get values
//Date
var dlDt = $("#<%=tbRchngDt.ClientID%>").val();
//Qty
var dlQty = $("#<%=tbQty.ClientID%>").val();
//item
var dlItem = $("#<%=ddlItem.ClientID%>").val();
//DO No
var dlDOId = $("#<%=ddlDO.ClientID%>").val();
var dlDO = $("#<%=ddlDO.ClientID%> option:selected").text();
//Week
var dlWeek = $("#<%=ddlWeek.ClientID%>").val();
//Dealer
var dlDealer = $("#<%=ddlDealer.ClientID%>").val();
DistributionDtl = new Object();
DistributionDtl.DOId = dlDOId;
DistributionDtl.DONo = dlDO;
DistributionDtl.DealerCode = dlDealer;
DistributionDtl.Week = dlWeek;
DistributionDtl.Item = dlItem;
DistributionDtl.Qty = dlQty;
DistributionDtl.Date = dlDt;
//alert(DistributionDtl);
arrobj.push(DistributionDtl);
JSONString = JSON.stringify(arrobj);
//alert(JSONString);
$("#list").jqGrid('setGridParam',
{ datatype: "local",
data: JSONString
}).trigger("reloadGrid");
});
jQuery("#list").jqGrid({ data: JSONString,
datatype: "local",
height: 150,
width: 600,
rowNum: 10,
rowList: [10, 20, 30],
colNames: ['DOID', 'Item', 'Qty', 'Date'],
colModel: [{ name: 'DOId', index: 'DOId', width: 60, sorttype: "int" },
{ name: 'Item', index: 'Item', width: 120 },
{ name: 'Qty', index: 'Qty', width: 80 },
{ name: 'Date', index: 'Date', width: 120}],
pager: "#pager",
viewrecords: true,
caption: "Contacts"
});
});
You should add data local to jqgrid with any of the following methods:
addJSONData - with data as json
addRowData - adding row by row and then trigger reload grid to recalculate pagination - data should be a javascript object
Documentation can be found here. Edit: According to the documentation you CAN set local data directly in the "data" param but it should be an array not a json string and you do not have the contents of JSONString in your question, so the problem might come from that.