Polymer 1.x: Defining "google" import - javascript

In this jsBin, I want to declaratively pre-load my element with a value of ['Colorado', 'South Dakota'] for the selected property.
When I open this jsBin, I expect to see the following:
✅ A successfully loaded page.
✅ An outline map of the U.S.
✅ A print of the selected variable in the upper left reading Colorado,South Dakota.
❌ The states of Colorado and South Dakota pre-selected and colored blue.
❌ No errors in the console.
Instead, I actually see the following:
✅ A successfully loaded page.
✅ An outline map of the U.S.
✅ A print of the selected variable in the upper left reading Colorado,South Dakota.
❌ The states of Colorado and South Dakota are not pre-selected or colored blue.
❌ The following console error:
console.error
google is not defined
Question
How can I get the value of google to be defined at the crucial location?
I have isolated the problem down to the following lines of code.
http://jsbin.com/zomejajibe/1/edit?html,console,output
_drawChart: function() {
console.log('A'); // Successfully logs to console
// The key is to get "google" defined at the following line
var dataTable = this.$.geochart._createDataTable(this.items); // "google" is not defined???
console.log('dataTable: '); // Does not successfully log because crashes above
console.log(dataTable);
this.$.geochart._chartObject.draw(dataTable, this.$.geochart.options);
},

So you have problem that the select changed trigger before the element ready.
I added timeout on the function _selectedChanged of 10 milisec so it push to the end of the event loop. You can change it to 0 milisec..
look http://jsbin.com/quzuyuwaha/1/edit?html,console,output

Specifically, #Alon added the following function:
http://jsbin.com/hulusijaje/1/edit?html,console,output
try: function(e) {
var self = this;
setTimeout(function() {
self._selectedChanged(e)
}.bind(self), 100)
},
And the full code of the solution is as follows:
http://jsbin.com/hulusijaje/1/edit?html,console,output
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<link href="google-chart/google-chart.html" rel="import"> </head>
<body>
<dom-module id="x-element"> <template>
<style>
google-chart {
width: 100%;
}
</style>
<button on-tap="_show">Show</button>
<div>[[selected]]</div>
<google-chart
id="geochart"
type="geo"
options="{{options}}"
data="{{items}}"
xon-google-chart-select="_onGoogleChartSelect"></google-chart>
</template>
<script>
(function() {
Polymer({
is: 'x-element',
/** /
* Fired when user selects chart item.
*
* #event us-map-select
* #param {object} detail Alpabetized array of selected state names.
/**/
properties: {
selected: {
type: Array,
notify: true,
//reflectToAttribute: true,
},
items: {
type: Array,
notify: true,
reflectToAttribute: true,
value: function() {
return [
['State', 'Select'],
['Alabama', 0],
['Alaska', 0],
['Arizona', 0],
['Arkansas', 0],
['California', 0],
['Colorado', 0],
['Connecticut', 0],
['Delaware', 0],
['Florida', 0],
['Georgia', 0],
['Hawaii', 0],
['Idaho', 0],
['Illinois', 0],
['Indiana', 0],
['Iowa', 0],
['Kansas', 0],
['Kentucky', 0],
['Louisiana', 0],
['Maine', 0],
['Maryland', 0],
['Massachusetts', 0],
['Michigan', 0],
['Minnesota', 0],
['Mississippi', 0],
['Missouri', 0],
['Montana', 0],
['Nebraska', 0],
['Nevada', 0],
['New Hampshire', 0],
['New Jersey', 0],
['New Mexico', 0],
['New York', 0],
['North Carolina', 0],
['North Dakota', 0],
['Ohio', 0],
['Oklahoma', 0],
['Oregon', 0],
['Pennsylvania', 0],
['Rhode Island', 0],
['South Carolina', 0],
['South Dakota', 0],
['Tennessee', 0],
['Texas', 0],
['Utah', 0],
['Vermont', 0],
['Virginia', 0],
['Washington', 0],
['West Virginia', 0],
['Wisconsin', 0],
['Wyoming', 0]
];
},
},
color: {
type: String, // '#455A64'
value: function() {
return 'blue';
}
},
options: {
type: Object,
notify: true,
reflectToAttribute: true,
computed: '_computeOptions(color)',
},
itemIndices: {
type: Object,
computed: '_computeItemIndices(items)',
},
},
observers: [
// '_selectedChanged(selected.*)',
'try(selected.*)'
//'_drawChart(items.*, options)',
],
ready: function() {
var _this = this;
this.$.geochart.addEventListener('google-chart-select', function(e) {
this._onGoogleChartSelect(e)
}.bind(_this));
},
_computeItemIndices: function(a) {
var out = {},
i = a.length;
while (i--) {
out[a[i][0]] = i;
}
return out;
},
_onGoogleChartSelect: function(e) {
var s = e.path[0].textContent.split('Select')[0].trim(), // e.g. 'Ohio'
temp = [],
a = this.items,
index = this.itemIndices[s], // e.g. 35
i = a.length;
this.set('items.' + index + '.1', a[index][1] ? 0 : 1);
while (i-- - 1) {
/** /
if(s === a[i][0]){
this.set('items.' + i + '.1', a[i][1] ? 0 : 1);
//this.items[i][1] = a[i][1] ? 0 : 1;
}
/**/
if (a[i][1]) {
temp.push(a[i][0]);
}
}
temp.sort();
this.set('selected', temp);
this._drawChart();
//console.log(this.selected);
},
/**/
_redrawChart: function() {
var j = 1, // iterations
s = 1, // delay in seconds
_this = this;
while (j--) {
try {
// console.log(j);
_this._drawChart();
//return;
} catch (e) {
// console.log(e.message);
setTimeout(function() {
//console.log(j);
console.log(_this.selected); // undefined
}, (s * 1000));
_this._drawChart();
}
}
},
/**/
_drawChart: function() {
console.log('A'); // Successfully logs to console
// The key is to get "google" defined at the following line
try {
var dataTable = this.$.geochart._createDataTable(this.items); // "google" is not defined???
console.log('dataTable: '); // Does not successfully log because crashes above
// console.log(dataTable);
this.$.geochart._chartObject.draw(dataTable, this.$.geochart.options);
} catch (e) {}
},
doAll: function(verb) {
verb = verb || 'clear'; // verb: 'clear'(default)|'select'
verb = (verb === 'select') ? 'select' : 'clear';
this._doAll(verb);
this._drawChart();
},
_doAll: function(verb) {
var resetSelect = (verb && verb === 'some') ? false : true;
verb = verb || 'clear'; // verb: 'clear'(default)|'select'|'some'
verb = (verb === 'select') ? 'select' : 'clear';
var temp = [];
var items = this.items,
i = items.length;
switch (verb) {
case 'select':
while (i-- - 1) {
items[i][1] = 1;
temp.push(items[i][0]);
}
break;
case 'clear':
while (i-- - 1) {
items[i][1] = 0;
}
break;
default:
break;
}
this.set('items', items);
if (resetSelect) {
temp.sort();
this.set('selected', temp);
}
},
try: function(e) {
var self = this;
setTimeout(function() {
self._selectedChanged(e)
}.bind(self), 100)
}, ////
_selectedChanged: function(e, k) {
console.log('selected');
if (k === undefined) {
console.log(e);
//// return
} else {
console.log(k);
}
var a = e.base,
i = a.length;
this._doAll('some');
while (i--) {
var index = this.itemIndices[a[i]];
this.set('items.' + index + '.1', 1);
}
this._redrawChart();
this.fire('us-map-select', this.selected);
// console.log(this.selected);//
// console.log(this.items);
},
_computeOptions: function(s) {
return {
region: 'US',
displayMode: 'regions',
resolution: 'provinces',
legend: 'none',
defaultColor: 'white',
colorAxis: {
colors: ['#E0E0E0', s],
minValue: 0,
maxValue: 1,
}
}
},
_show: function() {
//this.set('selected', ['Ohio', 'New Mexico']);
this.doAll();
//console.log(this.itemIndices);
},
});
})();
</script>
</dom-module>
<x-element xcolor="#455A64" selected='["Colorado", "South Dakota"]'></x-element>
</body>
</html>

Related

How to avoid variable scope issues within a map function?

I have a working solution of this problem but I'm trying to make a cleaner and neat version of it as much as possible. I came up with another solution that uses a function within a map function. Unfortunately, this version has a few issues and I want to just know why the second solution is not working. I'm guessing it's a variable scope issue here. I'm looking forward to know your opinion about it.
I have a simple function that prints calendar days in an array!
So a question is why the first version of my code get the expected results while the second version prints unexpected results.
I tried to change let to var and I also made the counter and startedIndexing outside the function scope.
Solution 1 (works):
const currentFullMonth = {
days_length: 31,
first_day: "Thu",
first_day_index: 4,
last_day: "Sat",
last_day_index: 6,
month: "Aug",
year: 2019
}
const testMonth = [
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]
];
function printMonthCalender(month) {
let counter = 0;
let startedIdxing = false;
return month.map(week => {
return week.map((day, index) => {
if (index === currentFullMonth.first_day_index && !startedIdxing) {
counter++;
startedIdxing = true;
return counter;
} else if (startedIdxing) {
if (currentFullMonth.days_length === counter) {
counter = 0;
}
counter++;
return counter;
} else {
return 0;
}
});
});
} // end of Solution #1 <-- this works :)
Solution 2 (doesn't work):
// start of Solution #2 <-- does not work :(
// im using two functions to make it look more cleaner
//
function printMonthCalender2(month) {
let counter = 0;
let startedIdxing = false;
return month.map(week => {
return week.map((day, index) =>
indexingMonth(counter, startedIdxing, index)
);
});
}
function indexingMonth(counter, startedIdxing, index) {
if (index === currentFullMonth.first_day_index && !startedIdxing) {
counter++;
startedIdxing = true;
return counter;
} else if (startedIdxing) {
if (currentFullMonth.days_length === counter) {
counter = 0;
}
counter++;
return counter;
} else {
return 0;
}
}// end of Solution #2
console.log(printMonthCalender(testMonth));
console.log(printMonthCalender2(testMonth));
expected result as follows (first version):
[0, 0, 0, 0, 1, 2, 3]
[4, 5, 6, 7, 8, 9, 10]
[11, 12, 13, 14, 15, 16, 17]
[18, 19, 20, 21, 22, 23, 24]
[25, 26, 27, 28, 29, 30, 31]
[1, 2, 3, 4, 5, 6, 7]
unexpected result as follows (second version):
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
The problem is that when you reassign startedIdxing inside of indexingMonth, it's a local variable, so it doesn't get changed inside of the calling function (printMonthCalender2).
An issue is that .map should not have mutation or reassignment as a side-effect. While you could tweak things so that indexingMonth returned something that you checked and then reassigned startedIdxing to, I'd prefer a different approach: create a flat array, eg
[0, 0, 0, 0, 1, 2, ..., 30, 31, 1, 2, 3]
and then chunk it into pieces of 7 afterwards:
const currentFullMonth = {
days_length: 31,
first_day: "Thu",
first_day_index: 4,
last_day: "Sat",
last_day_index: 6,
month: "Aug",
year: 2019
}
const makeZeroArr = length => new Array(length).fill(0);
const printMonthCalendar = (testMonth) => {
// Create array: [1, 2, 3, ..., 30, 31]
const oneMonth = Array.from(
{ length: currentFullMonth.days_length },
(_, i) => i + 1
);
// Create a flat array with leading zeros and trailing last week:
// [0, 0, 0, 0, 1, 2, 3, ..., 30, 31, 1, 2, 3, 4, 5, 6, 7]
const flatResultArr = [
...makeZeroArr(currentFullMonth.first_day_index),
...oneMonth,
...oneMonth // this includes extra numbers that will be trimmed
].slice(0, 7 * 6); // 7 days/week * 6 weeks
// Chunk the flat array into slices of 7:
const resultArr = [];
for (let i = 0; i < 7; i++) {
resultArr.push(flatResultArr.slice(i * 7, (i + 1) * 7));
}
return resultArr;
};
console.log(printMonthCalendar());
In functions, primitive types like numbers and booleans are passed by value, not by reference. So when you define counter and startedIdxing in printMonthCalender2 and then try to change them in indexingMonth, the changes get lost as soon as you return to printMonthCalender2.
However in JavaScript, objects get passed by reference. So something like this would work:
function printMonthCalender2(month) {
let obj = { counter: 0, startedIdxing = false };
return month.map(week => {
return week.map((day, index) =>
indexingMonth(obj, index)
);
});
}
function indexingMonth(obj, index) {
if (index === currentFullMonth.first_day_index && !obj.startedIdxing) {
obj.counter++;
obj.startedIdxing = true;
return obj.counter;
} else if (obj.startedIdxing) {
if (currentFullMonth.days_length === obj.counter) {
obj.counter = 0;
}
obj.counter++;
return obj.counter;
} else {
return 0;
}
}// end of Solution #2
Things like obj.counter++ will actually keep those changes in your original object defined in printMonthCalender2.
Warning: While you can do this, if you are working with complex code this is often frowned upon. These kinds of mutations can be very difficult to debug if a problem occurs. It's a legitimate programming technique, but shouldn't be abused.
Also if you're working in a team that adheres to the functional programming paradigm, I believe this is a big no-no.
However given the very short duration and limited scope of the obj variable in this example, I would personally feel very comfortable with this. If obj had a much longer lifetime and was used in numerous places in the code then I'd be more wary of it, and would agree with #CertainPerformance's comment that a map statement shouldn't mutate things.

checkbox in google visualization table

Please refer to the following link:
http://jsfiddle.net/RjHMH/46/
I am using google visualization table, and making a tree table like above. Some column in child row, I attached html checkbox.
I question I am facing is that, if I click the checkbox, it is never checked. This is because in the table listener, every time a select event is triggered, it will redraw the table.
I look at the google visualization table API, and find this:
Note that the table chart only fires row selection events; however,
the code is generic, and can be used for row, column, and cell
selection events.
This means that if I click a column in row, I can never know which column I actually clicked? So I can not get the checkbox by id, and using javascript to make it checked? That sucks...
Indeed, getSelection() function does not preserve column once select event is triggered. But you could consider the following approach to preserve checkboxes states once the chart is redrawn.
First, we need to introduce object for storing checkboxes states:
var checkboxStates = {'cbox1' : false, 'cbox2': false};
Then we register ready event for saving/loading state once Google Chart is redrawn:
google.visualization.events.addOneTimeListener(table, 'ready', function(){
//...
});
And finally the following example demonstrates how to save/load state:
//load checkboxes state
for(var id in checkboxStates){
var checkbox = document.getElementById(id);
if(checkbox !== null) {
checkbox.checked = checkboxStates[id];
}
}
//save state
if(event.target.type == "checkbox"){
var checkbox = document.getElementById(event.target.id);
checkbox.checked = !event.target.checked;
checkboxStates[event.target.id] = checkbox.checked;
}
Note: Event.target is utilized to track checkboxes click events
Final example
Below is provided the modified example of yours with ability to preserve checkboxes state
google.load('visualization', '1', {
packages: ['table']
});
google.setOnLoadCallback(drawTable);
function drawTable() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'id');
data.addColumn('string', 'parentId');
data.addColumn('boolean', 'visible');
data.addColumn('number', 'level');
data.addColumn('string', 'Name');
data.addColumn('number', 'Value');
data.addRows([
['1', null, true, 1, 'Foo', 10],
['1.1', '1', false, 2, 'Foo 1', 2],
['1.1.1', '1.1', false, 3, 'Foo 1a', 2],
['1.1.2', '1.1', false, 3, 'Foo 1b', 2],
['1.2', '1', false, 2, 'Foo 2', 3],
['1.3', '1', false, 2, 'Foo 3', 5],
['1.3.1', '1.3', false, 3, '<input type="checkbox" id="cbox1" value="second_checkbox">', 1],
['1.3.2', '1.3', false, 3, '<input type="checkbox" id="cbox2" value="second_checkbox">', 4],
['2', null, true, 1, 'Bar', 14],
['2.1', '2', false, 2, 'Bar 1', 6],
['2.2', '2', false, 2, 'Bar 2', 7],
['2.2.1', '2.2', false, 3, 'Bar 2a', 3],
['2.2.2', '2.2', false, 3, 'Bar 2b', 2],
['2.2.3', '2.2', false, 3, 'Bar 2c', 2]
]);
// get all rows with children
// start by finding all child rows (ie, rows with parents)
var children = data.getFilteredRows([{
column: 1,
minValue: '1'
}]);
var parentsArray = [];
var parentId;
// identify the parents of all children
for (var i = 0; i < children.length; i++) {
parentId = data.getValue(children[i], 1);
if (parentsArray.indexOf(parentId) === -1) {
parentsArray.push(parentId);
}
}
//format the parent rows
var parent = data.getFilteredRows([{
column: 3,
value: 1
}]);
for (var j = 0; j < parent.length; j++) {
parentId = data.getValue(parent[j], 0);
if (parentsArray.indexOf(parentId) !== -1) {
data.setProperty(parent[j], 4, 'className', 'parentcl close');
}
else {
data.setProperty(parent[j], 4, 'className', 'parentcl');
}
};
//format the 2level rows
var leveltwo = data.getFilteredRows([{
column: 3,
value: 2
}]);
for (var j = 0; j < leveltwo.length; j++) {
parentId = data.getValue(leveltwo[j], 0);
if (parentsArray.indexOf(parentId) !== -1) {
data.setProperty(leveltwo[j], 4, 'className', 'leveltwo close');
}
else {
data.setProperty(leveltwo[j], 4, 'className', 'leveltwo');
}
};
//format the 3level rows
var levelthree = data.getFilteredRows([{
column: 3,
value: 3
}]);
for (var j = 0; j < levelthree.length; j++) {
data.setProperty(levelthree[j], 4, 'className', 'levelthree');
};
var view = new google.visualization.DataView(data);
// hide the first four columns
view.setColumns([4, 5]);
view.setRows(data.getFilteredRows([{
column: 2,
value: true
}]));
var table = new google.visualization.Table(document.getElementById('table_div'));
var cssClassNames = {
headerRow: 'gtableheader',
oddTableRow: 'rowodd',
headerCell: 'gtableheader'
};
var options = {
showRowNumber: false,
allowHtml: true,
cssClassNames: cssClassNames,
sort: 'disable'
};
var checkboxStates = {'cbox1' : false, 'cbox2': false};
google.visualization.events.addListener(table, 'select', function () {
var sel = table.getSelection();
recurseTree(view.getTableRowIndex(sel[0].row), false);
view.setRows(data.getFilteredRows([{
column: 2,
value: true
}]));
table.setSelection(null);
google.visualization.events.addOneTimeListener(table, 'ready', function(){
//load checkboxes state
for(var id in checkboxStates){
var checkbox = document.getElementById(id);
if(checkbox !== null) {
checkbox.checked = checkboxStates[id];
}
}
//update state
if(event.target.type == "checkbox"){
var checkbox = document.getElementById(event.target.id);
checkbox.checked = !event.target.checked;
checkboxStates[event.target.id] = checkbox.checked;
}
});
table.draw(view, options);
function recurseTree(row, hideOnly) {
// get the id of the row
var id = data.getValue(row, 0);
// get the parent row
var parentrow = data.getFilteredRows([{
column: 0,
value: id
}]);
var parentlevel = data.getValue(parentrow[0], 3);
// find all child rows
var rows = data.getFilteredRows([{
column: 1,
value: id
}]);
for (var i = 0; i < rows.length; i++) {
if (data.getValue(rows[i], 2)) {
// hide the row and recurse down the tree
data.setValue(rows[i], 2, false);
switch (parentlevel) {
case 1:
data.setProperty(parentrow[0], 4, 'className', 'parentcl close');
break;
case 2:
data.setProperty(parentrow[0], 4, 'className', 'leveltwo close');
break;
default:
data.setProperty(parentrow[0], 4, 'className', 'levelthree close');
}
recurseTree(rows[i], true);
}
else if (!hideOnly) {
// if the row is hidden, show it
data.setValue(rows[i], 2, true);
switch (parentlevel) {
case 1:
data.setProperty(parentrow[0], 4, 'className', 'parentcl open');
break;
case 2:
data.setProperty(parentrow[0], 4, 'className', 'leveltwo open');
break;
default:
data.setProperty(parentrow[0], 4, 'className', 'levelthree open');
}
}
}
}
});
table.draw(view, options);
}
.parentcl{
font-weight: bold !important;
}
.close:before{
content:"→ "
}
.open:before{
content:"↘ "
}
.leveltwo{
padding-left: 20px !important;
}
.levelthree{
padding-left: 45px !important;
font-style:italic;
}
.gtableheader {
font-weight: bold;
background-color: grey;
}
.rowodd {
background-color: beige;
}
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<div id="table_div"></div>

Javascript: Traverse lists of lists asynchronously

I'm trying to traverse a javascript lists of lists.
The goal is to pass each item into a function and process it
asynchronously. However it isn't working:
var tree = function (data, callback) {
var data_position = 0;
var iterate = function () {
if (data_position<data.length) {
if (Array.isArray(data[data_position])) {
tree(data[data_position], callback);
// If I uncomment these it will show all items but not ordered
//data_position++;
//iterate();
} else {
callback(data[data_position++], iterate);
}
}
}
iterate();
}
tree([1, 2, [100, 200, 300], 3, 4, 5, 6, 7, 8, 9], function (item, iterate) {
setTimeout(function(){
console.log('Item: ' + item);
iterate();
}, 1000);
})
The code stops at 300 instead of processing the rest of the tree.
If I uncomment those 2 lines above, I can print all items, but they
don't show ordered.
How can I fix this?
This solution uses a second optional callback that allows the child iterating function tree to signal the parent tree to continue running once the child is finished.
It runs as expected with 1 second delay between every leaf element iteration.
var tree = function (data, callback, done) {
var data_position = 0;
var iterate = function () {
if (data_position<data.length) {
if (Array.isArray(data[data_position])) {
tree(data[data_position++], callback, function() { iterate(); });
} else {
callback(data[data_position++], iterate);
}
} else if (done) {
done();
}
}
iterate();
};
tree([1, 2, [100, 200, 300], 3, 4, 5, 6, 7, 8, 9], function (item, iterate) {
setTimeout(function(){
console.log('Item: ' + item);
iterate();
}, 1000);
});
This should work:
var tree = function (data, callback) {
var data_position = 0;
var iterate = function () {
if (data_position<data.length) {
if (Array.isArray(data[data_position])) {
tree(data[data_position], callback);
}
callback(data[data_position++], iterate);
}
}
iterate();
}
tree([1, 2, [100, 200, 300], 3, 4, 5, 6, 7, 8, 9], function (item, iterate) {
setTimeout(function(){
if(!Array.isArray(item)) console.log('Item: ' + item);
iterate();
}, 1000);
})

Extracting multi-dimentsional arrays in Javascript/JQuery

I'm extracting some data from an SQL source, which I can get into a javascript script as a simple array (shown grouped by dates) which consists of week no, task number and hours spent:
mydata = [
// weekno, taskno, hours
["2014-14",160,37.5],
["2014-15",160,30],
["2014-15",243,7.5],
["2014-16",160,37.5],
["2014-17",0,7.5],
["2014-17",3,7.5],
["2014-17",321,22.5],
["2014-18",0,7.5],
["2014-18",321,30],
["2014-19",3,7.5],
["2014-19",295,30]
];
I'm going to be charting it using HighCharts, and I need to get it into two property arrays like this:
properties = {
categories: [ "2014-14","2014-15","2014-16","2014-17","2014-18","2014-19"],
series: [
// Task Week
// No 14 15 16 17 18 19
//
{ name: '0', data: [ 0, 0, 0, 7.5, 7.5, 0 ] },
{ name: '3', data: [ 0, 0, 0, 7.5, 0, 7.5 ] },
{ name: '160', data: [ 37.5, 30, 37.5, 0, 0, 0 ] },
{ name: '243', data: [ 0, 7.5, 0, 0, 0, 0 ] },
{ name: '295', data: [ 0, 0, 0, 0, 0, 30 ] },
{ name: '321', data: [ 0, 0, 0, 22.5, 30, 0 ] }
]
}
Aside from looping, am I missing some succinct, idiomatic method for doing this?
In case it's of use to anyone, here's a cobbled together solution:
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var categories = [];
var subcategories = [];
var temp = {};
for (var i = 0; i < myChartData.length; i++) {
key = myChartData[i][0];
taskno = myChartData[i][1];
hours = myChartData[i][2];
if (taskno in temp == false) temp[taskno] = {};
if (key in temp[taskno] == false) temp[taskno][key] = 0;
temp[taskno][key] += hours;
categories.push(myChartData[i][0]);
subcategories.push(myChartData[i][1])
}
var uniqueCategories = categories.filter(onlyUnique).sort();
var uniqueSubcategories = subcategories.filter(onlyUnique).sort(function(a, b) {
return a - b
});
var series = [];
for (var i = 0; i < uniqueSubcategories.length; i++) {
subcatKey = uniqueSubcategories[i];
series[i] = { name: 'Task ' + subcatKey, data: [] };
for (var j = 0; j < uniqueCategories.length; j++) {
catKey = uniqueCategories[j];
series[i]['data'].push(temp[subcatKey][catKey] ? temp[subcatKey][catKey] : 0);
}
}
where series and uniqueCategories are the required data.

Change array of object to CSV pattern

i have this following Array :
var objRow = [
{
2011-09-20 : [0, 100, 0],
customerID : C1101,
ANI : 1234
},
{
2011-09-25 : [0, 0, 0],
customerID : C1101,
ANI : 1234
},
{
2011-09-20 : [0, 500, 0],
customerID : C1102,
ANI : 5678
},
{
2011-09-22 : [0, 0, 50],
customerID : C1102,
ANI : 5678
}
]
I want to create CSV Data from array above. But, i have problem to change that array to this CSV pattern :
1234, C1101, 0, 0, 100, 0, 0, 0
5678, C1102, 0, 0, 500, 0, 0, 50
I try to group the customerID using reduce, and because the first index in every object is date. I have some array of dates :
var dateArr = ["2011-09-20", "2011-09-22", "2011-09-25"];
And this is my code :
var result = objRow.reduce(function(prev, curr, index, arr) {
var num = curr["customerID"];
if (!prev[num]) {
prev[num] = [];
}
for (var j = 0; j < dateArr.length; j++) {
prev[num].push(curr[dateArr[j]]);
}
return prev;
}, {});
Update Question
For number combination in date index. I use this rules :
[0, 100, 0] // from first Object
[0, 0, 0] // from second Object
fistObject_firstIndex, secondObject_firstIndex, firstObject_secondIndex, secondObject_secondIndex, firstObject_thirdIndex, secondObject_thirdIndex
0, 0, 100, 0, 0, 0
Up, Down, Up, Down, Up, Down...
How to create CSV Pattern above?
Thank you...
I think this will give you the result you want:
var objRow = [{
date: 2011-09-20,
nums: [0, 100, 0],
customerID: "C1101",
ANI: 1234
}, {
date: 2011-09-25,
nums: [0, 0, 0],
customerID: "C1101",
ANI: 1234
}, {
date: 2011-09-20,
nums: [0, 500, 0],
customerID: "C1102",
ANI: 5678
}, {
date: 2011-09-22,
nums: [0, 0, 50],
customerID: "C1102",
ANI: 5678
}];
//CREATE CSV-FORMATTED STRINGS
var csvLine = "";
var numsArray = new Array();
for (var i=0; i<objRow.length; i++) {
//check if this is the first element with a new 'ANI' (which means a new CSV line starts)
if (objRow[i-1]==(undefined||null) || objRow[i].ANI!=objRow[i-1].ANI) {
//if so, start a new string
csvLine = objRow[i].ANI +", "+ objRow[i].customerID +", "; //add the 'ANI' and 'customerID'
numsArray.length = 0; //clear array
numsArray.push(objRow[i].nums); //store the 'nums' in a separate array
} else {
//if not, add to the existing string
numsArray.push(objRow[i].nums); //store the 'nums' in a separate array
}
//check if this is the last element with the same 'ANI' (which means this CSV line is complete)
if (objRow[i+1]==(undefined||null) || objRow[i].ANI!=objRow[i+1].ANI) {
//add the 'nums' of every object in intertwining order (every 1st, every 2nd, etc.)
for (var k=0; k<numsArray[0].length; k++) {
for (var j=0; j<numsArray.length; j++) {
csvLine += numsArray[j][k].toString() +", ";
}
}
//remove the last comma
if (csvLine.substring(csvLine.length-2) == ", ") {
csvLine = csvLine.substring(0,csvLine.length-2);
}
//output the CSV line
document.getElementById("csv").innerHTML += csvLine + "<br />";
}
}
<div id="csv"></div>
(fiddle: http://jsfiddle.net/5gyp3ce6/16/)
I had to change your array a little bit, because for this to work, the array keys need to all be the same.
Also, I had to change the ID's to strings, otherwise they couldn't be defined.
Instead of writing it to the <div> at the end you can of course add the line to another variable of write it to file or whatever.
If the comments in the code aren't clear enough, just leave a comment and I'll try to explain it better.
Try
var objRow = [
{
"2011-09-20" : [0, 100, 0],
customerID : "C1101",
ANI : 1234
},
{
"2011-09-25" : [0, 0, 0],
customerID : "C1101",
ANI : 1234
},
{
"2011-09-20" : [0, 500, 0],
customerID : "C1102",
ANI : 5678
},
{
"2011-09-22" : [0, 0, 50],
customerID : "C1102",
ANI : 5678
}
];
var arr = [],
res = [],
csv = $.map(objRow, function (v, k) {
// items
arr.push(v.ANI, v.customerID, v[Object.keys(v)[0]]);
// arrays
var a = $.grep(arr, function (val, index) {
return $.isArray(val)
});
// strings
var s = arr.filter(function (i) {
return typeof i === "string"
});
// sort items
res.push([arr.filter(Number)[0]
, s[0]
, a.splice(0, 2).join(",")
, arr.filter(Number).slice(-1)[0]
, s.slice(-1)[0]
, a.join(",")]);
return res
}).slice(-1)[0];
// format text , html
csv = (csv.slice(0, 3) + "<br>" + csv.slice(-3))
.replace(/,/g, ", ");
$("body").append(csv)
var objRow = [
{
"2011-09-20" : [0, 100, 0],
customerID : "C1101",
ANI : 1234
},
{
"2011-09-25" : [0, 0, 0],
customerID : "C1101",
ANI : 1234
},
{
"2011-09-20" : [0, 500, 0],
customerID : "C1102",
ANI : 5678
},
{
"2011-09-22" : [0, 0, 50],
customerID : "C1102",
ANI : 5678
}
];
var arr = [],
res = [],
csv = $.map(objRow, function (v, k) {
arr.push(v.ANI, v.customerID, v[Object.keys(v)[0]]);
// arrays
var a = $.grep(arr, function (val, index) {
return $.isArray(val)
});
// strings
var s = arr.filter(function (i) {
return typeof i === "string"
});
res.push([arr.filter(Number)[0], s[0], a.splice(0, 2).join(","), arr.filter(Number).slice(-1)[0], s.slice(-1)[0], a.join(",")]);
return res
}).slice(-1)[0];
csv = (csv.slice(0, 3) + "<br>" + csv.slice(-3))
.replace(/,/g, ", ");
$("body").append(csv)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Categories