d3js: Calculating sub-totals, conditional on i=="some stuff" - d3.js

Warning/Disclaimer: This is a basic JavaScript question, but I've gone through a bunch of iterations in my code, much Googling, and I'm having trouble wrapping my head around how to proceed.
I have data in three columns in a CSV file: a political candidate's name, their party, and their approval rating.
I've created a bubble chart/force layout, similar to this. Candidates are represented as bubbles. Users can select to see candidates organized in one big blob, or they can click to see the bubbles organize themselves by party. I have some <div> elements that pop up under each grouping of same-party candidates. What I'd like to do now is to have each party-specific <div> element display the total approval rating all its same-party candidates get, combined. (So, in Excel, a =SUMIF().)
To do this: I'm creating a function(party) which should, in principle, return that conditional sum. Here's what it looks like when I'm calling it for candidates with no party affiliation:
d3.select("#text-NONE")
.text(label_party("N/A"));
"N/A" is the string found in the CSV file.
And the function itself:
function label_party(party) {
var party_total = 0;
function party(d) {
if (d.party[i] == "party") {
party_total+= d.y2012[i];
};
};
return party_total;
};
Both of the above are happening outside of the d3.csv() call. My main Q: how can I set up a conditional sum over two columns in a CSV? At the moment, it's simply returning 0 - so it's skipping my loop, though I don't know why.

Related

Is it possible to work with more than one array in vuetify v-autocomplete?

Normally one would just make a simple join to merge both arrays in one array, the problem is that i have arrays with different object structures, and depending on the type of object, i need to pass a different value.
Example:
array 1: fruits.type.name
array 2: animals.family.name
Is there any possibility other than having to craft a custom component from scratch using something like v-text-input, for example?
You mean something like this? Check this codesanbox I made:
https://codesandbox.io/s/stack-71429578-switch-autocomplete-array-757lve?file=/src/components/Example.vue
computed: {
autoArray() {
return this.typeAnimal ? this.animals : this.fruits
},
autoTypeId() {
return this.typeAnimal ? 'family.id' : 'type.id'
},
autoText() {
return this.typeAnimal ? 'family.name' : 'type.name'
}
}
With help of a couple computed props you could be able to switch array, item-text and item-value depending of the array you're working with.
As far as I know, there's no easy way to supply two different arrays to v-autocomplete and retain the search functionality.
You could probably join the arrays and write a custom filter property. Then use selection and item slots to change the output of the select based on the structure.
But if your data arrays aren't too complicated, I would avoid the above. Instead, I would loop through both arrays, and build a new combined one with a coherent structure.

Look up Enum by label instead of by value

Is there a quick way to look up an Enum with only using the label of the enum instead of the value. Let's say the Enum type is SalesStatus, I want to be able to basically call into some kind of a function like enumLabel2Value(enumStr(SalesStatus), "Open order") and it will return 1.
I'm trying to avoid looping thru all possible values and checking each one separately, it seems like this should be something that's readily available since whenever a user filters on a enum column on a grid, they enter in the label, not the value, but I haven't seen anything like it.
You can use the str2Enum function for that. From the documentation:
Retrieves the enum element whose localized Label property value
matches the input string.
In addition to the caveats from Alex Kwitny's answer, I recommend taking a look at the comments of the documentation, specifically the comment
Please note that str2Enum performs partial matching and matches the
beginning of the string. If there are multiple matches, it will take
the first one.
In addition take a look at method string2Enum of class DMFEntityBase, which supports different options how the enum element can be specified. I think with the DictEnum.name2Value() method enum elements specified by their label are handled.
Update
OP mentioned in the comments to Alex Kwitny's answer that it is a specific enum ExchangeRateDisplayFactor he has issues with. str2Enum also works with that enum, as the following job demonstrates:
static void str2EnumTest(Args _args)
{
ExchangeRateDisplayFactor factor;
factor = str2Enum(factor, '1');
info(strFmt('%1', factor)); // outputs '1'
factor = str2Enum(factor, '10');
info(strFmt('%1', factor)); // outputs '10'
factor = str2Enum(factor, '100');
info(strFmt('%1', factor)); // outputs '100'
factor = str2Enum(factor, '1000');
info(strFmt('%1', factor)); // outputs '1000'
factor = str2Enum(factor, '10000');
info(strFmt('%1', factor)); // outputs '10000'
}
It doesn't exist because labels can be all sorts of things in different languages. symbol2Value() exists though and may be what you're looking for, but your question is specifically on labels. An example of where this could be very bad...
Let's say you have an enum called GoodBadPresent, to indicate what type of Christmas present you will receive, with two values:
GoodBadPresent::Poison English label: "Poison"; German label: "Gift"
GoodBadPresent::Gift English label: "Gift"; German label: "Geschenk"
If this example isn't clear, the word for Poison in German is Gift. So if you tried to resolve Gift to an enum value, you'd also have to provide the language. The performance problems here are probably greater than the performance problems of looping through an enum.
You can look at DictEnum to see if there are any methods that can help you more succinctly achieve what you want though. https://msdn.microsoft.com/en-us/library/gg837824.aspx
I'm more curious to the details of your scenario where you need to get back to an enum from a label.

Returning other values from d3.call

Per the docs, "The call operator always returns the current selection, regardless of the return value of the specified function." I'd like to know if there is a variant of call or reasonable workaround for getting call-behavior that returns values other than the selection.
Motivation:
I've got a chart and a datebrush, each encapsulated in a function
function trends_datebrush() {
// Setup
function chart(_selection) {
_selection.each(function(_data) {
// Do things
...});
}
return chart;
};
(The chart follows a similar format but isn't called datebrush).
These are instantiated with:
d3.select("someDiv")
.datum("data")
.call(trends_datebrush());
// And then we call the chart
I'd like to return a subselection from brush to be used as the data variable in the chart call. As is I need to make them both aware of some higher order global state, which gets messy especially since I want other control functions to drill down on the data. If I could override call, then I could do something like
d3.select("someDiv")
.datum("data")
.call(trends_datebrush())
.call(trends_chart());
And then if I were to implement some new filter I could throw it into the chain with another call statement.
tl;DR: Looking for ways to get chain chart calls s.t. they can pass transformed data to each other. I want monadic D3 charts! Except I don't really know monads so I might be misusing the word.

Order $each by name

I am trying to figure why my ajax $each alters the way my list of names gets printed?
I have an json string like this:
[{"name":"Adam","len":1,"cid":2},{"name":"Bo","len":1,"cid":1},{"name":"Bob","len":1,"cid":3},{"name":"Chris","len":1,"cid":7},{"name":"James","len":1,"cid":5},{"name":"Michael","len":1,"cid":6},{"name":"Nick","len":1,"cid":4},{"name":"OJ","len":1,"cid":8}]
Here all the names are sorted in alphabetic order, but when getting them out they are sorted by "cid"? Why, and how can I change this?
Here is my jQuery:
var names = {};
$.getJSON('http://mypage.com/json/names.php', function(data){
$.each(data.courses, function (k, vali) {
names[vali.cid] = vali.name;
});
});
I guess its because "names[vali.cid]", but I need that part to stay that way. Can it still be done?
Hoping for help and thanks in advance :-.)
Ordering inside an object is not really defined or predictable when you iterate over it. I would suggest sorting the array based on an internal property:
var names = [];
$.getJSON('http://mypage.com/json/names.php', function(data){
$.each(data.courses, function (k, vali) {
names.push({name: vali.name, cid: vali.cid});
});
names.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
});
Now you have an array that is ordered and you can iterate over it in a predictable order as well.
There is no "ajax $each" - you probably mean the jQuery function.
With "when getting them out" I presume you mean something like console.debug(names) after your $each call
Objects aren't ordered in javascript per definition, so there is no more order in your variable "names". Still, most javascript implementations today (and all the ones probably important to you - the ones used in the most used browsers) employ a stable order in objects which normally depends on the order you insert stuff.
All this said, there can probably be 3 reasons you're not getting what you're expecting:
Try console.debug(data) and see what you get - the order as you want it?
As you don't explicitly state how you debug your stuff, the problem could be in the way you output and not the data is stored. Here too try console.debug(names).
You're using a function which dereferences on expection, like console.*. This means if you console.debug() an object, the displayed values will depend on the moment you unfold the displayed tree in your browser, not when the line was called!

Sorting CouchDB Views By Value

I'm testing out CouchDB to see how it could handle logging some search results. What I'd like to do is produce a view where I can produce the top queries from the results. At the moment I have something like this:
Example document portion
{
"query": "+dangerous +dogs",
"hits": "123"
}
Map function
(Not exactly what I need/want but it's good enough for testing)
function(doc) {
if (doc.query) {
var split = doc.query.split(" ");
for (var i in split) {
emit(split[i], 1);
}
}
}
Reduce Function
function (key, values, rereduce) {
return sum(values);
}
Now this will get me results in a format where a query term is the key and the count for that term on the right, which is great. But I'd like it ordered by the value, not the key. From the sounds of it, this is not yet possible with CouchDB.
So does anyone have any ideas of how I can get a view where I have an ordered version of the query terms & their related counts? I'm very new to CouchDB and I just can't think of how I'd write the functions needed.
It is true that there is no dead-simple answer. There are several patterns however.
http://wiki.apache.org/couchdb/View_Snippets#Retrieve_the_top_N_tags. I do not personally like this because they acknowledge that it is a brittle solution, and the code is not relaxing-looking.
Avi's answer, which is to sort in-memory in your application.
couchdb-lucene which it seems everybody finds themselves needing eventually!
What I like is what Chris said in Avi's quote. Relax. In CouchDB, databases are lightweight and excel at giving you a unique perspective of your data. These days, the buzz is all about filtered replication which is all about slicing out subsets of your data to put in a separate DB.
Anyway, the basics are simple. You take your .rows from the view output and you insert it into a separate DB which simply emits keyed on the count. An additional trick is to write a very simple _list function. Lists "render" the raw couch output into different formats. Your _list function should output
{ "docs":
[ {..view row1...},
{..view row2...},
{..etc...}
]
}
What that will do is format the view output exactly the way the _bulk_docs API requires it. Now you can pipe curl directly into another curl:
curl host:5984/db/_design/myapp/_list/bulkdocs_formatter/query_popularity \
| curl -X POST host:5984/popularity_sorter/_design/myapp/_view/by_count
In fact, if your list function can handle all the docs, you may just have it sort them itself and return them to the client sorted.
This came up on the CouchDB-user mailing list, and Chris Anderson, one of the primary developers, wrote:
This is a common request, but not supported directly by CouchDB's
views -- to do this you'll need to copy the group-reduce query to
another database, and build a view to sort by value.
This is a tradeoff we make in favor of dynamic range queries and
incremental indexes.
I needed to do this recently as well, and I ended up doing it in my app tier. This is easy to do in JavaScript:
db.view('mydesigndoc', 'myview', {'group':true}, function(err, data) {
if (err) throw new Error(JSON.stringify(err));
data.rows.sort(function(a, b) {
return a.value - b.value;
});
data.rows.reverse(); // optional, depending on your needs
// do something with the data…
});
This example runs in Node.js and uses node-couchdb, but it could easily be adapted to run in a browser or another JavaScript environment. And of course the concept is portable to any programming language/environment.
HTH!
This is an old question but I feel it still deserves a decent answer (I spent at least 20 minutes on searching for the correct answer...)
I disapprove of the other suggestions in the answers here and feel that they are unsatisfactory. Especially I don't like the suggestion to sort the rows in the applicative layer, as it doesn't scale well and doesn't deal with a case where you need to limit the result set in the DB.
The better approach that I came across is suggested in this thread and it posits that if you need to sort the values in the query you should add them into the key set and then query the key using a range - specifying a desired key and loosening the value range. For example if your key is composed of country, state and city:
emit([doc.address.country,doc.address.state, doc.address.city], doc);
Then you query just the country and get free sorting on the rest of the key components:
startkey=["US"]&endkey=["US",{}]
In case you also need to reverse the order - note that simple defining descending: true will not suffice. You actually need to reverse the start and end key order, i.e.:
startkey=["US",{}]&endkey=["US"]
See more reference at this great source.
I'm unsure about the 1 you have as your returned result, but I'm positive this should do the trick:
emit([doc.hits, split[i]], 1);
The rules of sorting are defined in the docs.
Based on Avi's answer, I came up with this Couchdb list function that worked for my needs, which is simply a report of most-popular events (key=event name, value=attendees).
ddoc.lists.eventPopularity = function(req, res) {
start({ headers : { "Content-type" : "text/plain" } });
var data = []
while(row = getRow()) {
data.push(row);
}
data.sort(function(a, b){
return a.value - b.value;
}).reverse();
for(i in data) {
send(data[i].value + ': ' + data[i].key + "\n");
}
}
For reference, here's the corresponding view function:
ddoc.views.eventPopularity = {
map : function(doc) {
if(doc.type == 'user') {
for(i in doc.events) {
emit(doc.events[i].event_name, 1);
}
}
},
reduce : '_count'
}
And the output of the list function (snipped):
165: Design-Driven Innovation: How Designers Facilitate the Dialog
165: Are Your Customers a Crowd or a Community?
164: Social Media Mythbusters
163: Don't Be Afraid Of Creativity! Anything Can Happen
159: Do Agencies Need to Think Like Software Companies?
158: Customer Experience: Future Trends & Insights
156: The Accidental Writer: Great Web Copy for Everyone
155: Why Everything is Amazing But Nobody is Happy
Every solution above will break couchdb performance I think. I am very new to this database. As I know couchdb views prepare results before it's being queried. It seems we need to prepare results manually. For example each search term will reside in database with hit counts. And when somebody searches, its search terms will be looked up and increments hit count. When we want to see search term popularity, it will emit (hitcount, searchterm) pair.
The Link Retrieve_the_top_N_tags seems to be broken, but I found another solution here.
Quoting the dev who wrote that solution:
rather than returning the results keyed by the tag in the map step, I would emit every occurrence of every tag instead. Then in the reduce step, I would calculate the aggregation values grouped by tag using a hash, transform it into an array, sort it, and choose the top 3.
As stated in the comments, the only problem would be in case of a long tail:
Problem is that you have to be careful with the number of tags you obtain; if the result is bigger than 500 bytes, you'll have couchdb complaining about it, since "reduce has to effectively reduce". 3 or 6 or even 20 tags shouldn't be a problem, though.
It worked perfectly for me, check the link to see the code !

Resources