I'm converting existing code that displays a single line graph to display multiple line graphs at once. I would like each line graph to have tooltips. My code looks like this:
var line = new RGraph.Line('canvas', data).Set('tooltips', tips)
I can make multiple line graphs render by changing the data array from 1-dimension to 2-dimensions.
But I have also converted the tips array to 2-dimensions, and no tooltips are appearing at all.
Am I correct in assuming that the tips array is assumed to be 1-dimensional, and if so how do I specify tooltips for multiple line charts?
Give them as one big array. If you have them in multiple arrays you can use the JavaScript concat() function or you just add them together like below.
Here's an example without using the concat function:
new RGraph.Line({
id: 'cvs',
data: [
[8,4,3],
[9,8,1]
],
options: {
tooltips: ['A', 'B','C','D','E','F']
}
}).draw();
And here's an example using concat():
tooltips_a = ['A', 'B','C'];
tooltips_b = ['D','E','F'];
tooltips_combo = tooltips_a.concat(tooltips_b);
new RGraph.Line({
id: 'cvs',
data: [
[8,4,3],
[9,8,1]
],
options: {
tooltips: tooltips_combo
}
}).draw();
Related
Given an array with objects of this form:
[{data: {set: 'set1', option: 'option1'}},
{data: {set: 'set1', option: 'option2'}},
{data: {set: 'set1', option: 'option3'}},
{data: {set: 'set2', option: 'optionA'}},
{data: {set: 'set2', option: 'optionB'}}]
How can I get an array that looks like this:
[{set: 'set1', options: ['option1', 'option2', 'option3']},
{set: 'set2', options: ['optionA', 'optionB']}]
I would like to use functional programming, Ramda or native JS methods. Thanks.
I like to think about questions like this in terms of a few steps that keep moving me toward my desired output. Sometimes this means that I miss a more elegant solution, but it usually makes it easier to come up with something that works.
So let's look at your problem this way using Ramda.
Step 1: Remove unnecessary data
You don't want that outer data property. Since your data is a list of objects, and each one has a single data property holding the data we want, we can simply call prop('data') on each one.
To call it on each one, we can use map, giving us map(prop('data')).
Because this is such a common use, Ramda also supplies a function which combines map and prop this way: pluck('data'). Applying that to your input we get:
[
{option: "option1", set: "set1"},
{option: "option2", set: "set1"},
{option: "option3", set: "set1"},
{option: "optionA", set: "set2"}
{option: "optionB", set: "set2"}
]
This is a good start. Now we need to think about combining them into like groups.
Step 2: Grouping the data
We want all the elements that share their set property to be grouped together. Ramda offers groupBy, which accepts a function that turns an item into a grouping key. We want to group by that set property, so we can use prop again, and call groupBy(prop('set')) against the previous results.
This yields:
{
set1: [
{option: "option1", set: "set1"},
{option: "option2", set: "set1"},
{option: "option3", set: "set1"},
],
set2: [
{option: "optionA", set: "set2"}
{option: "optionB", set: "set2"}
]
}
There is redundant information in there. Somewhere we're going to need to figure that out. But I'll save that for a little bit while I try to pull together other parts.
Step 3: Combine the options
We've already seen pluck. It looks like we could use it on set1 and set2. Well map also works on objects, so if we simply call map(pluck('option')) on that last we get this:
{
set1: ["option1", "option2", "option3"],
set2: ["optionA", "optionB"]
}
Oh look, that also got rid of the redundancy. This is looking pretty close to the desired output.
Step 4: Rebuilding Objects
But now I don't see a built-in Ramda function that will get me all the way there. I could write a custom one. Or I could look to convert this in two steps. Knowing that I would like to use Ramda's zipObj function, I can first convert the above to arrays via toPairs, generating this:
[
["set1", ["option1", "option2", "option3"]],
["set2", ["optionA", "optionB"]]
]
and then I can map zipObj over the results with the keys I want each property to have. That means I can call map(zipObj(['set', 'options'])) to get the final desired results:
[
{
set: "set1",
options: ["option1", "option2", "option3"]
},
{
set: "set2",
options: ["optionA", "optionB"]
}
]
Step 5: Putting it all together
All right, now we have to put these together. Ramda has pipe and compose. I usually choose compose only when it fits on one line. So merging these with pipe, we can write this:
const transform = pipe(
pluck('data'),
groupBy(prop('set')),
map(pluck('option')),
toPairs,
map(zipObj(['set', 'options']))
)
And then just call it as
transform(myObj)
You can see this in action on the Ramda REPL. On there you can comment out later lines inside the pipe to see what the earlier ones do.
I built the code there, adding one line at a time to the pipe until I had transformed the data. I think this is a nice way to work.
I'm trying to build an API in node.js that syncs two different systems, and I'm having trouble breaking down an array of objects I receive from my source system.
Basically, I receive an array that looks a bit like this:
sourcedata = {
"items": [
{ "id": "item1",
"some fields": {...array of some fields },
"more fields": {another block of fields}
},
{ "id": "item2",
"some fields": {...array of some fields },
"more fields": {another block of fields}
}]
}
What I also have are three arrays of IDs - new ones (means I have to send them to the target system), old ones (to delete from the target), and ones that appear on both - those I need to check a special tag to see if they are different or not.
e.g.
newitems = [id1,id2,id3]
existingItems [id4,id5,id6]
deletedItems = [id7,id8]
What I'm trying to do is create new arrays that will only contain the data of the NEW and EXISTING items, so I can process and send them over to the target system without scanning the sourcedata array for each key and deciding what to do . I know how to do that when I compare simple arrays, but here I need the entire objects and all its fields copied over, and I can't
find the proper way to do it. Any help would be appreciated.
You can make use of the filter function. It filters out elements of array based on your condition. You may use it with the includes function to determine if a thing is in an array. For example:
sourceArray = [
{
id: 'a'
},
{
id: 'b'
},
{
id: 'c'
},
{
id: 'd'
}
]
var toDelete = ['c', 'b']
sourceArray = sourceArray.filter(x => !toDelete.includes(x.id))
// some items in sourceArray have now been removed.
Note that filter and includes may require a recent version of nodejs.
I would like to add vertical lines to graph as markers for interesting events.
The only way i can see to add multiple lines is to define multiple controller.verticalLine annotations like
controller.verticalLine({
xAnchor: "2007-09-23"
});
controller.verticalLine({
xAnchor: "2008-10-23"
});
Is it possible to do this nicer like
controller.verticalLine([{
xAnchor: "2007-09-23"
},
{
xAnchor: "2007-10-23"
},
{
xAnchor: "2007-11-23"
}]);
or better, pass it a data set using mapAs, where each value in the mapping would be a xAnchor value ?
var mapping = dataTable.mapAs({"value": 4});
controller.verticalLine(mapping);
Thanks
You can define the lines from the dataset with the custom field and get them from data with the get() method:
https://api.anychart.com/latest/anychart.data.Mapping#get
mapping.get(i, "anchor"),
Like is shown here: https://jsfiddle.net/osub60ck/
The example i'm goint o use here is taken from http://autoform.meteor.com/quickform
I've been using autoform for a few projects, but i need to get this same behaviour without it, how could i do an insert of an Array of objects??
So here is the Schema definition you need with autoform
items: {
type: Array,
optional: true,
minCount: 0,
maxCount: 5
},
"items.$": {
type: Object
},
"items.$.name": {
type: String
},
"items.$.quantity": {
type: Number
}
Next is the call to autoform to generete the form in the template
{{> quickForm id="demo" schema=schemaFromJSON type="method" meteormethod="demoSubmission"}}
With that in place you get a form, displaying both fields: name and quantity, plus a sign for ading more objects with those same fields, and when you actually submit the form it inserts all of your objects.
The HTML and CSS is not the problem
I'm not quite sure what you are asking. These are two ways of inserting arrays to collection:
// insert items one by one
// NOTE: Meteor doesn't allow inserting of arrays like `Items.insert(items)`
items.forEach(item => Items.insert(item));
// insert all items simultaneously by using raw mongo collection:
Items.rawCollection().insert(items);
The specific example would be a "preferences" window that has a series of tabs, each of which contains a series of form fields. None of these tabs or form fields would be reused outside of the window, and there would only ever be one instance of the window itself. Using a single item config means it's hundreds of lines long, making it hard to maintain.
My.Ns.PreferencesWindow = Ext.extend(Ext.Window, {
items: [
// A tab
{
items: [
// Form fields
{},
{},
{},
]
},
// A tab
{
items: [
// Form fields
{},
{},
{},
]
},
...
]
});
I am not asking how to organize a large ExtJS application. I have read Best Way to Organize an ExtJS Project and Saki's blog posts on the topic.
It doesn't really make sense to Ext.extend for each of the items, because they're not going to be instantiated more than once.
Encapsulating the various components in "generator" functions that just return an item's json seems at first glance to be a reasonable approach.
Just use variables to make it more readable:
var tabOneCfg = {
items: [
// etc.
]
};
var tabTwoCfg = {
items: [
// etc.
]
};
My.Ns.PreferencesWindow = Ext.extend(Ext.Window, {
items: [
tabOneCfg,
tabTwoCfg
]
});
You can make it as granular as you want, and even include the sub-configs in separate files (although that type of scheme would not play well with dynamic loading under Ext 4). A generator or factory function could make sense as well, depending on the nature of the configs. In principle it's the same thing either way -- just break the sub-configs into smaller chunks and use them as needed.