Order $each by name - ajax

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!

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.

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.

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

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.

Rewriting .each() loop as for loop to optimize, how to replicate $(this).attr()

I running into some performance issues with a jquery script I wrote when running in IE so I'm going through it trying to optimize any way possible. Apparently using for loops is way faster than using the jQuery .each method. This has led me to a question regarding the equivalent of $(this) inside a for loop. I'm simplifying what I'm doing in my loop down to just using an attr() function as it gets across my main underlying question.
Im doing this with each(simplified)
var existing = $('#existing').find('div');
existing.each(function(){
console.log($(this).attr('id'));
});
And I've tried rewriting it as a for loop as such:
var existing = $('#existing').find('div');
for(var i = 0;i < existing.length;i++)
{
console.log(existing[i].attr('id'));
}
Its throwing an error saying:
Uncaught TypeError: Object #<HTMLDivElement> has no method 'attr'
You need existing.eq() to get jQuery object, existing[] gives you DOM object. The function attr() should be called with jQuery object but not with DOM (javascript) object.
var existing = $('#existing');
for(var i = 0;i < existing.length;i++)
{
console.log(existing.eq(i).attr('id'));
}
You can use each to get index without for loop.
existing.each(function(index, item){
alert(index);
alert(item);
});
To get the id of an element just do
existing[i].id
Note that you jQuery loop would also be faster as
existing.each(function(){
console.log(this.id);
});
More generally, you should not use attr('id'), especially if you're concerned by performances, as a DOM object has a property id.
.I have to ask you a question before I give my answer, why would you need to perform a loop on a single element, #existing is an Id not, therefore it's a unique element on your page.
you could simply do
$('#existing').prop('id');
In case your have more than one elements, you should be using a class or another attribute, if that is the case, you could use the following:
$.each('.existing',function(index,item){
console.log(item.prop('id'));
});
better use prop() insted of attr() as attr is deprecated

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