Grouping / counting in javascript using underscore.js - javascript

I am new to javascript (and to Stack Overflow) and I've encountered a problem I can't seem to solve. I am trying to generate a simple pie chart that shows the number of Projects for each value of Technology in my data. This is the kind of data I am working with:
[Project1, Java]
[Project2, Excel]
[Project3, SAS]
[Project4, Java]
The pie ratio in the example above would be 2:1:1.
The first part of my code loads the data and pushes it to an array, "techArray", that contains [project, tech]. This part works ok - I've verified it in a simplified version of the code.
I then want to group the array "techArray" and count the instances of each tech. To do so I'm using the Underscore library, as follows:
var chartData = [];
var techData = _.groupBy(techArray, 'tech');
_.each(techData, function(row) {
var techCount = row.length;
chartData = push( {
name: row[0].tech,
y: techCount
});
});
The script then renders the chartData array using highcharts. Again, I have verified that this section works using a simplified (ungrouped) version.
There must be an issue with the grouping/counting step outlined above because I am seeing no output, but I simply can't find where. I am basing my solution on the following worked example: Worked example.
If anyone can spot the error in what I've written, or propose another way of grouping the array, I'd be very grateful. This seems like it should be a simpler task than it's proving to be.

countBy could be used instead of groupBy:
var techArray = [
{ project: 'Project1', tech: 'Java'},
{ project: 'Project2', tech: 'Excel'},
{ project: 'Project3', tech: 'SAS'},
{ project: 'Project4', tech: 'Java'},
];
var counts = _.countBy(techArray,'tech');
This will return an object with the tech as properties and their value as the count:
{ Java: 2, Excel: 1, SAS: 1 }
To get the data in the form for highcharts use map instead of each:
var data = _.map(counts, function(value, key){
return {
name: key,
y: value
};
});

This should work
var techArray = [['Project1','Java'], ['Project2', 'excel'], ['Project3', 'Java']];
var chartData = [];
var techData = _.groupBy(techArray, function(item) {
return item[1];
});
_.each(techData, function(value, key) {
var techCount = value.length;
chartData.push({
name: key,
y: techCount
});
});
_.groupBy needs to either get a property name, or a function that returns the value being grouped. There is no tech property of an array, so you cant group by it. But, as our techArray is an array of tuples, we can pass a function _.groupBy that returns the value that we want to groupBy, namely the second item in each tuple.
chartData now looks like this:
[{
name: 'Java',
y: 2
}, {
name: 'excel',
y: 1
}]

Related

Accessing a javascript object in D3.js

A javascript data object (JSON notation) has been created with the following content:
"[
{"range":"Shape","values":[{"idx":0,"val":"Random"},{"idx":1,"val":"Line"},{"idx":2,"val":"Square"},{"idx":3,"val":"Circle"},{"idx":4,"val":"Oval"},{"idx":5,"val":"Egg"}]},
{"range":"Color","values":[{"idx":0,"val":"Red"},{"idx":1,"val":"Blue"},{"idx":2,"val":"Yellow"},{"idx":3,"val":"Green"},{"idx":4,"val":"Cyan"}]}
]"
In a next step the index of an ordinal value has to be found in this object. The function should find the index of the value 'Blue' in the range 'Color'.
So the function should have the meta scripting form
f("Color")("Blue")=1
What is the most elegant form to create such a function in the context of D3 and javascript?
Depending on your use case, it might make sense to convert the data structure to a different structure more suitable for direct access. E.g. you could convert your structure to
var data = {
Shape: ['Random', 'Line', ...],
// ...
};
and access it with
data['Shape'].indexOf('Line') // or data.Shape.indexOf('Line')
Or go even one step further and convert to
var data = {
Shape: {
Random: 0,
Line: 1,
// ...
},
// ...
};
and access it with
data['Shape']['Line'] // or data.Shape.Line
What the best solution is depends on the actual use case.
Converting the structure dynamically is pretty straight forward. Here is an example to convert it to the first suggestion:
var newData = {};
data.forEach(function(item) {
newData[item.range] =
item.values.map(function(value) { return value.val; });
});
This would also reduce redundancy (e.g. idx seems to correspond with the element index).
Would this work for you ?
var dataJson = '[ \
{"range":"Shape","values":[{"idx":0,"val":"Random"},{"idx":1,"val":"Line"},{"idx":2,"val":"Square"},{"idx":3,"val":"Circle"},{"idx":4,"val":"Oval"},{"idx":5,"val":"Egg"}]},\
{"range":"Color","values":[{"idx":0,"val":"Red"},{"idx":1,"val":"Blue"},{"idx":2,"val":"Yellow"},{"idx":3,"val":"Green"},{"idx":4,"val":"Cyan"}]}\
]';
var data = JSON.parse(dataJson);
for (each in data){
if ( (data[each].range) === 'Color'){
for (eachVal in data[each].values){
if (data[each].values[eachVal].val === 'Blue'){
alert(data[each].values[eachVal].idx);
}
}
} ;
}
And here is the JSFiddle for you too.

Add new key value pair to existing Firebase

This might be a pretty basic question, but so far I can't find the answer to my problem online after much googling. I have a firebase web app where the data structure is pretty simple. Initially, it's empty, like this:
fireRef {
}
I want to be able to add key value pairs where the key is created by the user and the value is just some text. For instance, the user would enter their name as the key, and the value as their age. Then I want to send that data to the server and have the firebase now look like this:
fireRef {
John : 25,
}
I can accomplish this one addition with:
var name = getUserName();
var age = getUserAge();
var node = {};
node[name] = age;
fireRef.set(node);
However, I want multiple people to be able to do this. When I try to add a new person to the server, the old "John : 25" pair turns red and disappears, leaving only the new key value pair.
How can I keep both around, and maintain a dataset of a bunch of key, value pairs?
The unique id in firebase is generated when we push data.
For example:
var fireRef = new Firebase('https://<CHANGE_APP_NAME>.firebaseio.com/fireRef');
var newUserRef = fireRef.push();
newUserRef.set({ 'name': 'fred', 'age': '32' });
Another way is to directly set the child elements:
var fireRef = new Firebase('https://<CHANGE_APP_NAME>.firebaseio.com/fireRef');
fireRef.child(1).set({'name':'user2','age':'34'});
fireRef.child(2).set({'name':'user3','age':'24'});
#user3749797, I got confused with this exact problem.
#learningloop offered a good solution, because it achieves the task of adding data to your firebase, but there is an option to add a new k,v (name, age) pair into a single JSON associative array rather than push to an array of associative arrays. Effectively, #learningloop sees:
[
{
name: steve,
age: 34
},
{
name: mary,
age: 22
}
]
Perhaps his way is better, but you and I were looking for this:
{
steve: 34,
mary: 22
}
I've managed to add to this list of k,v pairs with
var fireRef = new Firebase('https://<CHANGE_APP_NAME>.firebaseio.com/fireRef');
fireRef.update({ 'jobe': '33'});
Yielding
{
steve: 34,
mary: 22,
jobe: 33
}
In my firebase.
Full documentation on saving to firebase [here]
Just to add bit more clarity to #Charlie's answer:
Following works for me:
admin.database().ref(SITE).child("ChildName").child("2ndLevelChild") .update({[KeyVariable]:valueVariable});
Aparently if I were to use it like the following it doesnt work (it overwrites:
var newChild = {};
newChild["ChildName/" + "2ndLevelChild"] = {
[KeyVariable]: valueVariable
};
admin.database().ref(SITE).update(newChild);

Traverse nested maps / objects from keys in an array

I think a code sample is going to work a lot better than my vocabulary:
var keys = ['folder','name'];
var data = { folder: { name: 'Special Folder' } };
Given the two vars above, I'm looking for a way to dynamically use the array as a way to look up the object keys (sort of like a "path"). So I need to programmatically produce the following:
data['folder']['name'] // that would give me 'Special Folder'
Hopefully this makes sense, I just can't quite put all the pieces together.
TIA
var keys = ['folder','name'];
var data = { folder: { name: 'Special Folder' } };
for(var i=0;i<keys.length;i++){
data = data[keys[i]];
}
alert(data)

Updating D3.js (NVD3.js) Data by Merge

I'm working on some realtime graphs built with NVD3.js. I currently refresh each chart with the following:
function reDraw(c) {
d3.json(c.qs, function(data) {
d3.select(c.svg)
.datum(data)
.transition().duration(500)
.call(c.chart);
});
}
c looks like:
function Chart(svg, qs, chart) {
this.qs = qs;
this.svg = svg;
this.ylabel;
this.chart;
}
This works fairly well, but with each refresh I am fetching the whole time series again. It would be more efficient to only grab recent elements and update each graph. There are examples of doing this by appending elements (This answer NVD3 line chart with realtime data and this tutorial for example) , but this isn't ideal for me since some recent elements might be updated that are not the most recent element.
So what I'm looking to do is grab say the most recent minute (by setting query string (.qs) to only get the most recent minute, then take that result and do the following:
Overwrite any existing elements that have the same x value for each series with the most recent data
Append and elements when there are new x values from the update in each series
Expire elements past a certain age
Update the NVD3.js script with the new data. Maybe still use datum with the new merged object?
Can anyone suggest an elegant way to perform the above Merge operation? The existing data object looks like the following:
> d3.select(perf.svg).data()[0]
[
Object
key: "TrAvg"
values: Array[181]
__proto__: Object
,
Object
key: "RedisDurationMsAvg"
values: Array[181]
__proto__: Object
,
Object
key: "SqlDurationMsAvg"
values: Array[181]
__proto__: Object
]
> d3.select(perf.svg).data()[0][0]['values'][0]
Object {x: 1373979220000, y: 22, series: 0}
> d3.select(perf.svg).data()[0][1]['values'][0]
Object {x: 1373979650000, y: 2, series: 1}
The object returned would look something like the following (Except will only be maybe 6 elements or so for each object):
> d3.json(perf.qs, function(data) { foo = data })
Object {header: function, mimeType: function, response: function, get: function, post: function…}
> foo
[
Object
,
Object
,
Object
]
> foo[0]
Object {key: "TrAvg", values: Array[181]}
> foo[0]['values'][0]
Object {x: 1373980220000, y: 49}
In this newer object the series value is missing - maybe that needs to get added or perhaps D3 can do it?
For the time being I used linq.js to perform this operation, and then use .datum() to bind a new dataset each time. The solution isn't very elegant but it does seem to function:
function reDraw2(c, partial) {
if (partial) {
qs = c.qs.replace(/minago=[0-9]+/, "minago=1")
} else {
qs = c.qs
}
d3.json(qs, function(new_data) {
chart = d3.select(c.svg)
if (c.ctype == "ts" && partial) {
thirtyminago = new Date().getTime() - (60*30*1000);
old_data = chart.data()[0]
var union = new Array();
for (var i = 0; i < old_data.length; i++) {
var existing = Enumerable.From(old_data[i]['values']);
var update = Enumerable.From(new_data[i]['values']);
oldfoo = existing
newfoo = update
var temp = {}
temp['key'] = old_data[i]['key']
temp['values'] = update.Union(existing, "$.x").Where("$.x >" + thirtyminago).OrderBy("$.x").Select().ToArray();
union[i] = temp
}
chart.datum(union)
} else {
chart.datum(new_data)
}
chart.call(c.chart)
});

Dictionary equivalent data structure?

I'm working in JavaScript and want to keep a list of set km/mph approximations to hand. (I can't convert programmatically, I'm working with an external API that expects certain values, so it really does have to be a dictionary equivalent.)
Currently I'm using an object:
var KM_MPH = { 10: 16, 12: 20, 15: 24 };
Going from mph to km is pretty easy:
var km = KM_MPH[10];
How do I find mph, given km? Also, is an object the best data structure to use for this sort of thing in JavaScript? I'm more used to Python.
A basic JavaScript object is in fact the best choice here. To find a reverse mapping, you can do:
function mphToKM(val){
for(var km in KM_MPH){
if(KM_MPH[km] === val){
return km;
}
}
return null;
}
Or, if you anticipate having to do a lot of lookups, I would recommend having a secondary JS Object that is the mirror of the first
var mph_km = {};
for(var km in KM_MPH){
mph_km[KM_MPH[km]] = km;
}
// mph_km[16] ==> 10
I don't know if you are in fact doing this for conversion between kilometres per hour to miles per hour... if so, it seems to make more sense to just do the conversion directly instead of relying on a hash mapping of the values.
var conversionRate = 1.609344; // kilometres per mile
function kphToMPH(val){
return val / conversionRate ;
}
function mphToKPH(val){
return val * conversionRate;
}
You can use iterate over all entries to find to find your key
Mostly a dict is used to from key=>value
Alternatively you can have two lists
var km = [];
var mph = [];
with their corresponding indices mapped
This is much closer to a Dictionary data structure, since you can have dozens of elements:
var dictionary = [
{ key: 10, value: 12 },
{ key: 12, value: 20 },
{ key: 15, value: 24 }
];
Then you can also use some JavaScript Framework like jQuery to filter elements:
var element = $.filter(dictionary, function() {
return $(this).attr("key") == 10;
});
alert($(element).attr("value"));
Yes, the JavaScript object is the correct choice.
Create a second object to do the reverse lookup:
var i, MPH_KM = {};
for(i in KM_MPH) MPH_KM[KM_MPH[i]] = i;
var mph = MPH_KM[16];
The dictionary equivalent structure for a javascript object would look like this:
var dictionary = { keys:[], values:[] };
Above structure is an equivalent of
Dictionary(Of Type, Type) **For VB.Net**
Dictionary<Type, Type>) **For C#.Net**
Hope this helps!

Categories

Resources