Flare ItemVisualisation - actionscript-3

Using the latest Flare build originally built by prefuse, I am trying to get an indent field like the one in Layouts seen here. I am working with a list of objects that I pull from Google Firebase. While I can put them on a graph just fine and compare one and other values I can't find instructions on the different layouts. I am about to study the docs but I wanted to know if there was anything else out there I could reference.
Edit:
http://flare.prefuse.org/api/flare/vis/operator/layout/Layout.html I have found the general layouts here. However I only am able to show 1 or two circles unless I do AxisLayout.
For reference, my data pulled from firebase is something like this.
An array of objects.
Each object has properties name, sales, date, active and such.
I want it to act like the example above and show the item name in each circle. Then when the user clicks the circle he is able to show the properties of the item.
Edit: I was able to find an article on it, but after hours of constructing my data around his set format, I am not able to mock it entirely.
http://simon.oconnorlamb.com/ria/2012/03/visualising-data-with-flare/
Edit: To go into detail:
When I pull from my json list from Google Firebase I parse it so that it puts the items in referencable categories. All of which go into an array to mock the tutorial I linked above. I am trying to mock the structure as close as possible.
private function handleDataRead(e:DatabaseEvent):void
{
var trueDataArray:Array = new Array();
//Extract manufacturers.
var manufacturers:Vector.<String> = new Vector.<String>();
for each (var item:Object in e.data)
{
if (manufacturers.indexOf(item.Manufacturer) == -1)
{
manufacturers.push(item.Manufacturer);
//Example: {type:'Manufacturer',id:'0',name:'Company A'}
trueDataArray.push( {type:'manufacturer',
id:manufacturers.indexOf(item.Manufacturer).toString(),
name:item.Manufacturer});
}
}
//Extract Item Name
var itemNames:Vector.<String> = new Vector.<String>();
for each (var item:Object in e.data)
{
if (itemNames.indexOf(item.ItemName) == -1)
{
itemNames.push(item.ItemName);
var idValue:String = new String(itemNames.indexOf(item.ItemName) + (manufacturers.length - 1) +1);
trueDataArray.push( {type:'item',
id:idValue,
name:item.ItemName,
manufacturer:manufacturers.indexOf(item.Manufacturer).toString()} );
}
}
//Extract property 1
var mlCount:Vector.<int> = new Vector.<int>();
for each (var item:Object in e.data)
{
if (item.hasOwnProperty("ML"))
{
if (mlCount.indexOf(item.ML) == -1)
{
mlCount.push(item.ML);
var idValue:String = new String(mlCount.indexOf(item.ML) + (itemNames.length - 1) + (manufacturers.length - 1) +1);
trueDataArray.push({type:'mL',
id:idValue,
name:(item.ML as int).toString(),
item:itemNames.indexOf(item.ItemName).toString()});
}
}
}
//Extract another property
var mgCount:Vector.<int> = new Vector.<int>();
for each (var item:Object in e.data)
{
if (item.hasOwnProperty("MG"))
{
if (mgCount.indexOf(item.MG) == -1)
{
mgCount.push(item.MG);
var idValue:String = new String(mgCount.indexOf(item.MG) + mlCount.indexOf(item.ML) + (itemNames.length - 1) + (manufacturers.length - 1) +1);
trueDataArray.push({type:'mG',
id:idValue,
name:(item.MG as int).toString(),
mL:mlCount.indexOf(item.ML).toString()});
}
}
}
The result looks like this.
[
{
"name":"Company A",
"type":"manufacturer",
"id":"0"
},
{
"name":"Company B",
"type":"manufacturer",
"id":"1"
},
{
"name":"Company C",
"type":"manufacturer",
"id":"2"
},
{
"name":"Company D",
"type":"manufacturer",
"id":"3"
},
{
"name":"Company E",
"type":"manufacturer",
"id":"4"
},
{
"type":"manufacturer",
"id":"5"
},
... //So on
{
"manufacturer":"0",
"name":"Item Name 1",
"type":"item",
"id":"18"
},
{
"manufacturer":"0",
"name":"Item Name 2",
"type":"item",
"id":"19"
},
{
"manufacturer":"0",
"name":"Item Name 3",
"type":"item",
"id":"20"
},
{
"manufacturer":"0",
"name":"Item Name 4",
"type":"item",
"id":"21"
...//So on
{
"name":"60",
"item":"0",
"type":"mL",
"id":"195"
},
{
"name":"100",
"item":"5",
"type":"mL",
"id":"196"
},
{
"name":"120",
"item":"36",
"type":"mL",
"id":"197"
},
{
"name":"30",
"item":"100",
"type":"mL",
"id":"198"
}
...//and so forth
]
When I pass this to my function to create the nodes and edges (that I again based off the blog in the link above)
public function buildTree(arr:Array):Data
{
var d:Data = new Data(true);
//Keyed lookup for easy edge addition in step 2
var nodeLookup:Object = {};
var row:Object;
var ns:NodeSprite;
//Step 1: Add all rows of data;
for each(row in arr){
ns = d.addNode(row);
nodeLookup[row.id] = ns;
}
//Step 2: Add edges
for each(ns in d.nodes){
if(ns.data.hasOwnProperty('manufacturer')){
d.addEdgeFor(nodeLookup[ns.data.manufacturer],ns);
}
else if(ns.data.hasOwnProperty('item')){
d.addEdgeFor(nodeLookup[ns.data.item],ns);
}
else if(ns.data.hasOwnProperty('mL')){
d.addEdgeFor(nodeLookup[ns.data.mL],ns);
}
}
return d;
}
and construct it
data = buildTree(trueDataArray);
sourceTree = new ItemVisualisation(data);
sourceTree.bounds = new Rectangle(10, 10, 550, 550);
sourceTree.x = 20;
sourceTree.y = 20;
addChild(sourceTree);
sourceTree.operators.add(new IndentedTreeLayout());
sourceTree.operators.add(new ShapeEncoder("data.type"));
sourceTree.operators.add(new ColorEncoder("data.type", Data.NODES, "lineColor", ScaleType.CATEGORIES));
sourceTree.data.nodes.setProperties({fillColor:0, lineWidth:2});
sourceTree.update();
I get the following..
I almost have this down but I don't know what I am doing wrong. Everything seems to be as it should in relation.
Edit: It seems that the nodes may be linking properly with each other however this is not the layout I desire. I cannot get any other layouts to work either.

trueDataArray.push({type:'root', id:0, name:'rootname'});
I was able to solve this problem by binding everything to 1 node. I was having some trouble listing anything beyond two steps but that is beyond the requirement of my project.

Related

How to get value of List Items using Google Apps Script

I have google document which contains list items. Now I want to get the values of those list items. For example, document contains this:
Text New
Text Old
How can I get list items (1,2)?
I used the following code, but it just gives me an array of list item objects but not the values of these list items.
function docFunction() {
var doc = DocumentApp.getActiveDocument();
var docBody = doc.getBody();
var docText = docBody.getListItems();
Logger.log(docText);
}
There is not way to retrieve the values you are needing.
Moreover, ListItem refers to the item in the list itself, not the Glyph.
The closest thing to getting the information you want is by using the getGlyphType which will return the type of glyph you are using, in this case NUMBER.
Reference
Apps Script ListItem Class - getGlyphType().
I believe your goal is as follows.
You want to retrieve the glyph from the list items by searching the text.
For example, when there is the following list,
1. Text New
2. Text Old
By searching Text New and Text Old, you want to retrieve the glyphs of 1. and 2..
Issue and workaround:
Unfortunately, in the current stage, it seems that the glyph cannot be directly retrieved. This has already been mentioned in the comment and the existing answer.
Fortunately, the glyph type can be retrieved by Google Document service. And, the glyph format can be retrieved Google Docs API. I thought that when these are used, a workaround might be able to be proposed. So, this is just my try. I'm not sure whether this method can be used for all situations. So please be careful about this.
Sample script:
This script uses Docs API. So please enable Docs API at Advanced Google services.
function myFunction() {
// Please set the searching texts in the list.
// const searchText = ["One One One", "Two Two Two", "ii ii ii ii ii", "C", "Factory Floor"];
// In this case, all list itmes are retrieved.
const searchText = DocumentApp.getActiveDocument().getBody().getListItems().map(e => e.getText().trim());
// This is an object for the numbers with the GlyphType. When you want to use more numbers and GlyphTypes, please add them.
const numObj = {
"NUMBER": ["1", "2", "3", "4", "5", "6"],
"LATIN_LOWER": ["a", "b", "c", "d", "e", "f"],
"LATIN_UPPER": ["A", "B", "C", "D", "E", "F"],
"ROMAN_LOWER": ["i", "ii", "iii", "iv", "v", "vi"],
"ROMAN_UPPER": ["I", "II", "III", "IV", "V", "VI"],
"GLYPH_TYPE_UNSPECIFIED": ["", "", "", "", "", ""],
"noGlyphType": ["", "", "", "", "", ""],
};
// 1. Retrieve list object using Docs API.
const doc = DocumentApp.getActiveDocument();
const listObj = Docs.Documents.get(doc.getId(), { fields: "lists" }).lists;
// 2. Retrieve list items using Document service and create an object for searching the texts.
const listItems = doc.getBody().getListItems();
const obj = listItems.reduce((o, e) => {
const listId = e.getListId();
const glyphType = e.getGlyphType() || "GLYPH_TYPE_UNSPECIFIED";
const nestingLevel = e.getNestingLevel().toString();
const text = e.getText();
if (o[listId]) {
if (o[listId][nestingLevel]) {
if (o[listId][nestingLevel][glyphType]) {
o[listId][nestingLevel][glyphType].text.push(text);
} else {
o[listId][nestingLevel] = { [glyphType]: { text: [text], glyphFormat: listObj[listId].listProperties.nestingLevels[nestingLevel].glyphFormat, nestingLevel } };
}
} else {
o[listId][nestingLevel] = { [glyphType]: { text: [text], glyphFormat: listObj[listId].listProperties.nestingLevels[nestingLevel].glyphFormat, nestingLevel } };
}
} else {
o[listId] = { [nestingLevel]: { [glyphType]: { text: [text], glyphFormat: listObj[listId].listProperties.nestingLevels[nestingLevel].glyphFormat, nestingLevel } } };
}
return o;
}, {});
// 3. Search text and output an object for retrieving glyphs.
const getRes = (obj, search, res = [], parents = []) => {
Object.entries(obj).forEach(([k, v]) => {
if (Array.isArray(v)) {
v = v.map(vv => vv.trim());
const temp = search.filter(s => v.includes(s));
if (temp.length > 0) {
const parent = parents[parents.length - 1];
res.push(temp.map(e => ({ search: e, k: parent.k == "null" ? "noGlyphType" : parent.k, idx: v.indexOf(e), glyphFormat: parent.v.glyphFormat, nestingLevel: parent.v.nestingLevel })));
}
} else if (typeof v == "object") {
parents.push({ k, v });
getRes(v, search, res, parents);
}
});
return search.map(s => res.flat().filter(t => t.search == s)[0]);
}
const res = getRes(obj, searchText).map(e => {
const count = [...e.glyphFormat].reduce((c, e) => e == "%" ? c + 1 : c, 0);
return e ? e.glyphFormat.replace(/%([0-9]+)/g, (_, p, o) => count > 1 ? numObj[e.k][o == 0 ? Number(p) : e.idx] : numObj[e.k][e.idx]) : "Text was not found."
});
console.log(res);
}
Testing:
When the above script is run, all list items are retrieved and the glyphs are returned. And, for example, when your sample Document shown in the following image is used and const searchText = ["One One One", "Two Two Two", "ii ii ii ii ii", "C", "Factory Floor"]; is used as the search texts,
the following result is obtained.
[ '1.', '2.', 'ii.', '(A)', '1.3' ]
Unfortunately, I couldn't find the method for identifying I of I1. and I2.. By this, the glyph of Factory Floor becomes 1.3.
Note:
In the current stage, in my proposed script, I couldn't find the method for identifying I of I1. and I2.. When I could find the method, I would like to update my sample script.
This is just my try. I'm not sure whether this method can be used for all situations. So please be careful about this.
When you want to retrieve the glyphs for all list items, please modify the above script as follows.
From
const searchText = ["One One One", "Two Two Two", "ii ii ii ii ii", "C", "Factory Floor"];
To
const searchText = DocumentApp.getActiveDocument().getBody().getListItems().map(e => e.getText().trim());
Unfortunately, in the current stage, it seems that the glyph type of check box type cannot be retrieved.
References:
getListItems()
Enum GlyphType
Method: documents.get
NestingLevel

How to set one more level in Object of a not predefined item?

The main issue is the following:
Get JSON from the server
Put data to form
Serialize form
Create JSON with correct structure
Send it to the server
I have difficulties on the fourth step, what I've done:
methods: {
onSubmit: function() {
var dataForm = new FormData(this.$refs['form']);
var data = [];
for (var _iterator = dataForm.entries(), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _Object$assign;
var _Helper = {};
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var _ref2 = _ref,
key = _ref2[0],
val = _ref2[1];
Object.assign(_Helper, (_Object$assign = {}, _Object$assign[key] = val, _Object$assign));
}
}
},
Here you go - a link to the codepen.
As you can see I could create JSON like that:
{"0":"a","1":"b","2":"c","3":"d","4":"e","5":"d"}
However, I need smth like that:
{
"0": {
"text": "a"
},
"1": {
"text": "b"
},
"2": {
"text": "c"
},
"3": {
"text": "d"
},
"4": {
"text": "e"
},
"5": {
"text": "d"
}
}
What Can I do to implement it and also keep the correct structure of my JSON?
To change the format, change in the location it assigns the property value:
Object.assign(data, (_Object$assign = {}, _Object$assign[key] = {text: val}, _Object$assign));
// -------------------------------------------------------------^^^^^^^^^^^
Instead of a string (val), assign the object in the format you want ({text: val}).
Updated CodePen.
If you can use modern (ES6) JavaScript, there's a much shorter notation for that:
var [key, val] = _ref;
Object.assign(data, {[key]: {text: val}});
CodePen here.
Or (because you are using FormData#entries() you do are using modern JS):
var formData = Array.from(new FormData(this.$refs['form']).entries());
var data = Object.assign(...formData.map(([key, val]) => ({[key]: {text: val}})));
console.log(JSON.stringify(data));
Targetting IE10 and above
To target IE10 and above you'll need polyfills. The problem is not the syntax, but the absence of functions like .entries(), that will be added by the polyfills. To use the least amount possible of polyfills, you'll have to iterate the iterator "manually" (kind of like you are already). For more info, check this answer.
You can do the whole construction much more simply:
onSubmit: function () {
const dataForm = new FormData(this.$refs['form']);
const data = {};
for (const i of dataForm.entries()) {
data[i[0]] = { text: i[1] }
}
console.log(JSON.stringify(data));
}

Loading multiple series into highcharts via JSON. How to parse?

I have a JSON source that look like this... ( I checked it at jsonlint and it appears valid )
[{
"day": "06/19/2016",
"region": "Ohio",
"daily_er": "98.61"
}, {
"day": "06/19/2016",
"region": "Western NE",
"daily_ef": "98.63"
}, {.........
I'm trying to load the day as the y Axis with the region as X Axis
In my experimentation with loading from a local CSV, I DID manage to get the below to work....
(data.csv)
Categories,06/19/16,06/20/16,06/21/16,06/22/16,06/23/16,06/24/16,06/25/16
Ohio,98.61,97.75,97.19,97.21,97.97,93.66,98.3
Western NE,98.63,98.42,98.25,98.27,98.29,98.35,98.24
$.get('data.csv', function(data) {
// Split the lines
var lines = data.split('\n');
// Iterate over the lines and add categories or series
$.each(lines, function(lineNo, line) {
var items = line.split(',');
// header line containes categories
if (lineNo == 0) {
$.each(items, function(itemNo, item) {
if (itemNo > 0) options.xAxis.categories.push(item);
});
}
else {
var series = {
data: []
};
$.each(items, function(itemNo, item) {
if (itemNo == 0) {
series.name = item;
} else {
series.data.push(parseFloat(item));
}
});
options.series.push(series);
}
});
// Create the chart
var chart = new Highcharts.Chart(options);
});
I want to apologize up front because while in my mind I get how CSV gets parsed, I so rarely work with JSON that I am stumped as to how I parse the JSON above. In my brain I'm getting hung up on dupes for the day values and how that's handled. Has anyone had an experience with this they'd like to share, maybe get me pointed in the right direction?
Many thanks!
JW

Unnesting json object

My sample json is
"multiList": [
{
"my_key" : "this is my key"
},
{
"my_text_box": "This is my text box"
},
]
How do I convert this to
{"my_key" : "this is my key"},
{my_text_box": "This is my text box"},
dynamically?
using jquery
Your question doesn't make sense. Are you asking to convert to two separate objects? A string representation of those two objects? Something else? I can do the first two:
var objOne = json.multiList[0];
var objTwo = json.multiList[1];
var objStr = JSON.stringify(json.multiList[0]) + ', '
+ JSON.stringify(json.multiList[1]);
If you want to add all of the separate properties into one object, you can just extend another object in a loop.
var obj = {};
json.multiList.forEach(function (elem) {
for (k in elem) {
if (elem.hasOwnProperty(k)) {
obj[k] = elem[k];
}
}
});
http://jsfiddle.net/ExplosionPIlls/t2xyd/
This makes no consideration for the overriding of properties in obj.

merge json object with arrays

Suppose I have two json objects and I need to merge them.
css: [{
drag: "mode() == 'layout'",
ui_draggable: "mode() == 'layout'"
}]
css: [{
someclass : true
}]
I want to end up with:
css: [{
drag: "mode() == 'layout'",
ui_draggable: "mode() == 'layout'",
someclass : true
}]
After some further trial I came up with this but I assume I have place a few bugs or useless lines of code in it.
I came up with this code after a little playing around. My needs didn't need to recurse more than two levels so this is just fine. It could be much refined I am sure but it works great for binding knockout. Here is an example of how I used it to extend jquery unobtrusive knockoutjs
var settings = {
text: 'SelectedCard().CardData.Title',
visible: "mode() != 'edit' || !isMyCard()",
css: [{ drag: "mode() == 'layout'" , selectedElement: "selectedCardElement() == '_titlePreview'"}]
};
var settings2 =
{
css: [{ drag: "mode() == 'layout'"}]
};
var settings3 = merge(settings, settings2);
function merge(first, second) {
for (var a1 in first) {
// if second object is null we are finished.
used.push(a1);
if (second[a1] == null) {
continue;
} else {
var ob2 = second[a1];
if (typeof (first[a1]) != typeof (ob2)) {
throw new Error("conflicting properties named:" + a1);
}
if ($.isArray(first[a1])) {
for (var i = 0; i < ob2.length; i++) {
first[a1].push(ob2[i]);
}
} else {
// matching property.
return merge(first[a1], second[a1]);
}
}
}
for (var a2 in second) {
if (used.indexOf(a2) < 0) {
first[a2] = second[a2];
}
}
return first;
}
1) To merge both objects with a one-way overwrite, this will do it:
for (var attrname in obj2) {
obj1[attrname] = obj2[attrname];
}
2) To merge selectively, both ways:
obj1.someclass = obj2.someclass;
-or-
obj2["someclass"] = obj1["someclass"];
In this case, if the property does not yet exist in the object it does not need to be defined before assigning it.
3) Consider using a library like Underscore.js for performing "array functions" similar to this:
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
returns [1, 2, 3, 101, 10]
4) Lastly, here's a strong resource for formatting JSON objects, arrays and a combination thereof: jsonexample.com. This will be helpful as you get into complex "array functions".
Cheers!