Sorting array function over JSON loops like crazy - json

so I'm trying to deal with a function but it's looping like crazy and I can't figure out why.
Basically, I want to loop over a json file, retrieve every "average" value and sort it in a new array, so when I call the function ranking(countries[iso].average), it returns the position in the array.
It's actually working but the json file is way bigger, and when I console.log(rank) in the loop, it returns more than 27K messages.
ranking = (n) => {
var rank = [];
if (n) {
for (let iso in countries) {
var newvar = countries[iso].average;
rank.push(newvar);
rank.sort(function(a, b) {
return b - a;
});
}
return rank.indexOf(n) + 1
}
};
{"countries":{"US":{"name":"United States of America","ranking":"","average":13.12,"flag":"https://restcountries.eu/data/usa.svg","altNames":["US","USA"],"reports":1302,"cases":0,"deaths":299692,"recovered":23232,"lat":38,"lng":-97,"deltaCases":2,"deltaDeaths":3,"deltaRecovered":0,"casesPerOneMillion":2,"deathsPerOneMillion":903,"totalTests":22323,"testsPerOneMillion":3434,"population":345},"IN":{"name":"India","ranking":"","average":10.22,"flag":"https://restcountries.eu/data/ind.svg","altNames":["IN","Bhārat"],"reports":1016,"cases":9796992,"deaths":142222,"recovered":9290834,"lat":20,"lng":77,"deltaCases":null,"deltaDeaths":null,"deltaRecovered":646,"casesPerOneMillion":7068,"deathsPerOneMillion":103,"totalTests":151632223,"testsPerOneMillion":109402,"population":1295210000},"RU":{"name":"Russian Federation","ranking":"","average":13.21,"flag":"https://restcountries.eu/data/rus.svg","altNames":["RU","Rossiya"],"reports":1321,"cases":2597711,"deaths":45893,"recovered":2059840,"lat":60,"lng":100,"deltaCases":28585,"deltaDeaths":613,"deltaRecovered":26171,"casesPerOneMillion":17797,"deathsPerOneMillion":314,"totalTests":81564365,"testsPerOneMillion":558804,"population":146599183}}}
Thanks for any help on this

I believe that what you may be trying to do is sort by the field called average for countries in the iso. So you have some lookup called countries and there are ISOs there like I imagine: 'US'. Then Rank is an array of all these countries.
The problem I see is that you have sort happening within the for loop.
The way you explained the problem seems like 2 different steps. One retrieve the average, then AFTER that sort by the average.
If really all you want is the averages in the array: you can do like
const averages = Object.values(countries).map(country => country.average)
That single step will get you all the averages into a single array.
Then next you can sort using the same function you posted. (The key is to brake that into a second loop not a nested loop:
averages.sort((a, b) => b - a)
// now sorted
But in case you wanted to keep the rest of the data you can do that pretty easily as well:
Something more like:
const countriesSortedByAverage = Object.values(countries).sort((a, b) => b.average - a.average)
If you really need the ISO you can also do the same with Object.entries but it might be even easier to provide the iso inside the country Object.
To determine the rank for all countries you can easily add that to (if you wanted) and have that be the principal country Object:
const RANKED_LIST_OF_COUNT = countriesSortedByAverage.map((countryObj, rank) => ({ ...countryObj, rank }))
If you want to further restore it to the CountriesByISO object:
const COUNTRIES_BY_ISO_WITH_RANK = Object.assign({}, ...RANKED_LIST_OF_COUNT.map(country => ({ [country.ISO]: country}))
)

Related

How to deal with Parsing Object from Join Query

I'm currently working on my first API with the Perfect framework. It's been a while since I made an API myself so I must admit my SQL and API logic is a little rusty.
I'm using a MySQL database for my implementation.
For sake of example I'll explain my database structure below;
I have a table which resembles an Object, let's call this Table A. Table A has a Varchar based id as primary key.
There are 2 other tables let's call them Table B and Table C. Table A has a one to many relation to both Table B and C. Where the id of table A is the foreign key.
What I'm trying to do is obtain everything with one query and cast it to an object in my backend.
By using outer joins I'm making the call to retrieve all the required data.
SELECT control.id, control.type, control.description, control.address, control.city, control.created, control.updated, control.latitude, control.longitude, images.id AS image_id, images.image, images.description AS image_description, updates.id AS update_id, updates.still_present, updates.created_at AS update_created
FROM Control control left outer join control_images images
ON control.id=images.control_id
left outer join Control_Updates updates
ON control.id=updates.control_id
Now is my question what would be the best way to store this data in an object that holds an array of updates and an array of images.
Before writing the join query I only attempted to get the values from Table A I used the following code to cast the results to my desired object.
let result = mysql.storeResults()
let checkResult = self.checkResult(result: result, response: response)
response = checkResult.response
var controls: [Control] = []
while let row = result?.next() {
let type = Types(rawValue: row[1].unwrap)!
let control = Control(id: row[0].unwrap, type: type, description: row[2].unwrap, address: row[3].unwrap, city: row[4].unwrap, latitude: Double(row[7].unwrap).unwrap, longitude: Double(row[8].unwrap).unwrap)
controls.append(control)
}
obviously this will just return duplicate objects apart from the images and updates of course.
I'm wondering if this is the best way to do it or if I should call a new query in the while loop
The best way to resolve this issue, by still only using one query and one loop is by using 'hashmaps'. I'm not familiar with Perfect framework, but in PHP it would look something like:
// Get results from the db:
$results = $db->execute($query, $params);
// Define map for controls:
$map = [];
// Loop over results/rows
foreach($results as $row){
// Get unique identifier for the Control model:
$controlId = $row['id'];
// Check if control is NOT already in map:
if(!isset($map[$controlId]){
// Add control to map:
$control = [
'id' => $controlId,
'description' => $row['description'],
'images' => []
// other fields
];
// Add control to map:
$map[$controlId] = $control;
}
else{
// Control exists, retrieve it from the map:
$control = $map[$controlId];
}
// Retrieve unique identifier of the image:
$imageId = $row['image_id'];
// Same tactic with hasmap, check if control already has the image, if not add it
if(!isset($control['images'][$imageId]){
// add the image to the hashmap:
}
else{
// Image is already added, the content from the 'update' data is not added yet, handle that part (also with a hash map)
}
}
Hope that helps you figure it out in Perfect framework

How to fetch json result in array using Ionic 2?

I am fetching data from JSON api using the below code in my 'listproduct.ts'
names = [];
this._listProduct.listProduct().subscribe(data => {
this.list = data;
console.log(data);
});
In console.log, I am fetching the result as expected, attaching a picture for your reference:
I need to fetch data.NAME in names array, how can I do this ?
data.NAME is not working.
As from the console, data is an array (of 10 elements).
When we want to convert an array to another array of the same size, 1-to-1 with some kind of "mapping", we can use the map() function.
Replace console.log(data); with this line names = data.map( e => e.NAME );
Check if it works ;)
Explanation
The function map() takes a function to be its argument, and one of the usages is to specify the element (e here) and how it corresponds to the result you want (we want e.NAME instead of the whole object e).
So e => e.NAME is a short form of e => { return e.NAME; } you may modify the function body as you might need in the future.
More information here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Adding multiple dynamic lines to highcharts

I was wondering if someone could help me out.
I have 2 sets of data being returned from an API which i need to chart to a line graph, the problem is i dont know which set of results will come at any given time, so i need something that will be able to chart both sets of the results.
The first set has the following info, ( taken from a var_dump )
'series' => string '[{name: 'Data'}]'
'data_lines' => string '[[[1473731108000,3.4804],[1473731406000,1.7047],[1473731704000,1.7559],[1473732004000,1.2774],[1473732304000,1.9295]]]'
The first number is a timestamp, and the second number is the plot point in GB, and the series name is the series name obviously.
The above set of results only needs one line, the second set of data is an averaging set of results and needs 3 lines, its response is as follows:
'series' => string '[{name: 'Data (average)'},{name: 'Data (maximum)'},{name: 'Data (minimum)'}]'
'data_lines' => string '[[[1473638400000,1.5094]],[[1473638400000,6.7825]],[[1473638400000,1.0546]]]'
and this one obviously has 3 data values for the 3 labels per timestamp.
Any help in getting this going would be greatly appreciated.
This is my sugestion: Create a function that will act like a router for your results. I don't know how you are processing your results (PHP or javascript), BTW:
function chartSelector($data) {
if (count($data['series']) == 3) {
//put the logic to process the averaging set of results here
} else {
//put the logic for the other set here
}
}
This function will direct the processing according to the given set of data :)

Getting data from thunk in node, mysql, koa

I want to get some data out of my MySQL database using Koa and the mysql node package. I was looking at co-mysql, but the readme suggests to use thunkify directly. So I did the following:
const query = thunkify(connection.query.bind(connection));
Which seems to work, as I now can do:
app.use(function * main() {
const races = yield query(
"SELECT * FROM `races` where '2016-01-19' between start_date and end_date"
)(function(err, rows) {
// rows is the data I need
});
});
However, I cannot find a way to return/yield the row data from the thunk into my races variable. I log it, and it displays the correct data, but when I try to pass it back, it always returns undefined. I've tried a couple of ways from inside the callback, but I can't seem to figure it out:
return rows
yield rows (made the callback a generator function)
return yield rows
...
I'm often getting: TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "undefined"
races is an array because you are using thunkify for query. co returns an array for any thunks that call their callback with more than one value (ie. callback(null, 1, 2, 3) returns [1, 2, 3].
If you were to Promisify query instead, races will be assigned to the first returned value only, which appears to be inline with what you're looking for.
Here's a code example showing it in practice:
var co = require("co")
var promisify = require("bluebird").promisify
var thunkify = require("thunkify")
function async(callback) {
callback(null, 1, 2, 3)
}
var p = promisify(async)
var t = thunkify(async)
co(function*() {
let x = yield p()
let y = yield t()
console.log(x)
console.log(y)
}).then(() => {})
When run, the value of x will be 1 and the value of y will be the array [1, 2, 3].
You can run it with Tonic here: https://tonicdev.com/56ab7cfc879afb0c002c1d49/56ab7cfc879afb0c002c1d4a

Couchbase rereduce questions

Here is coding from Couchbase Document and I dont understand it
function(key, values, rereduce) {
var result = {total: 0, count: 0};
for(i=0; i < values.length; i++) {
if(rereduce) {
result.total = result.total + values[i].total;
result.count = result.count + values[i].count;
} else {
result.total = sum(values);
result.count = values.length;
}
}
return(result);
}
rereduce means the current function call has already done the reduce or not. right?
the first argument of the reduce function, key, when will it be used? I saw a numbers of examples, key seems to be unused
When does rereduce return true and the array size is more than 1?
Again, When does rereduce return is false and the array size is more than 1?
Rereduce means that the reduce function is called before and now it is called again with params that were returnd as a result in first reduce call. So if we devide it into two functions it will look like:
function reduce(k,v){
// ... doing something with map results
// instead of returning result we must call rereduce function)
rereduce(null, result)
}
function rereduce(k,v){
// do something with first reduce result
}
In most cases rereduce will happen when you have 2 or more servers in cluster or you have a lot of items in your database and the calculation is done on multiple "nodes" of the B*Tree. Example with 2 servers will be easier to understand:
Let's imagine that your map function returned pairs: [key1-1, key2-2, key6-6] from 1st server and [key5-5,key7-7] from 2nd. You'll get 2 reduce function calls with:
reduce([key1,key2,key6],[1,2,6],false) and reduce([key5,key7],[5,7],false). Then if we just return values (do nothing in reduce, just return values), the reduce function will be called with such params: reduce(null, [[1,2,6],[5,7]], true). Here values will be an array of results that came from first reduce calls.
On rereduce key will be null. Values will be an array of values as returned by a previous reduce() function.
Array size depends only on your data. It not depends on rereduce variable. Same answer for 4th question.
You can just try to run examples from Views basics and Views with reduce. I.e. you can modify reduce function to see what it returns on each step:
function reduce(k,v,r){
if (!r){
// let reduce function return only one value:
return 1;
} else {
// and lets see what values have came in "rereduce"
return v;
}
}
I am also confused by the example from the official couchbase website as well, and below is what i thought.
confusion: the reduce method signature
1) its written as
function (keys, values, rereduce)
2) its written as function(key, values, rereduce)
What exactly is the first param, key or keys
For all my understand from my previous exp on the map/reduce, the key the key that emit from the map function and there is a hidden shuffle method that will aggregate the value into a value list for the same key.
So the key param can be an array under the circumstances that you emit an array as key (which you can use group by level control the level of aggregation)
So i am not agree with the example that given by #m03geek, it should not be a list of different keys, correct me if i am wrong.
My assumption:
Both reduce and rereduce work on the SAME key only.
eg:
reduce is like:
1)reduce(keyA, [1,2,3]) this is precalculated, and stored in Btree structure
2) rereduce(keyA, [6, reduce(keyA, [4,5,6])]), 6 is the sum of [1,2,3] from the first reduce method, then we add a new doc into couchbase, which will trigger the reduce method again, instead of calculating the whole thing again as the original map/reduce will do, couchbase get the precalculated data out from the btree which is 6, and run reduce from the key-value pairs from the map method (which is triggered by adding a new doc), then run re-reduce on the precalculated value + new value.