Confusion about d3.interpolateObject - javascript

I was just investigating the D3 Interpolate Object function, and I noticed some strange behavior. However, I'm not very familiar with D3, so it could be that I'm simply misunderstanding something. Given the following data and interpolation function:
var a = {"Country": "Ireland", "Year": 2010, "Data": 10};
var b = {"Country": "Ireland", "Year": 2015, "Data": 50};
var iFunc = d3.interpolateObject(a, b);
The following results are as expected:
console.log(iFunc(0.2)) // Returns: { Country: "Ireland", Year: 2011, Data: 18 }
console.log(iFunc(0.4)) // Returns: { Country: "Ireland", Year: 2012, Data: 26 }
However, when both function calls are included in the same console log, like this:
console.log(iFunc(0.2), iFunc(0.4))
The output is just the second Object twice:
{ Country: "Ireland", Year: 2012, Data: 26 } { Country: "Ireland", Year: 2012, Data: 26 }
And, when the function calls are put inside an array like so:
console.log([iFunc(0.2), iFunc(0.4)])
The previous output gets multiplied by two:
[{ Country: "Ireland", Year: 2014, Data: 42 }, { Country: "Ireland", Year: 2014, Data: 42 }]
What is going on here?
The reason I am investigating this is that I'd like to create a series of intermediate objects using something like:
var iVals = d3.range(0, 1, 0.2).map( iFunc );
If anybody can show me how I could achieve this, I'd really appreciate it!

This is an interesting problem. The explanation can be found in the own documentation:
Note: no defensive copy of the template object is created; modifications of the returned object may adversely affect subsequent evaluation of the interpolator. No copy is made for performance reasons; interpolators are often part of the inner loop of animated transitions. (emphasis mine)
As you can see, if you use the same interpolator you get the weird result you described (open your browser's console, don't use the snippet's one):
var a = {
"Country": "Ireland",
"Year": 2010,
"Data": 10
};
var b = {
"Country": "Ireland",
"Year": 2015,
"Data": 50
};
var iFunc = d3.interpolateObject(a, b);
var iVals = d3.range(0, 1, 0.2).map(iFunc);
console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>
So, the simplest solution is defining the interpolator function inside the map():
var iVals = d3.range(0, 1, 0.2).map(function(d) {
return d3.interpolateObject(a, b)(d)
});
Here is the demo:
var a = {
"Country": "Ireland",
"Year": 2010,
"Data": 10
};
var b = {
"Country": "Ireland",
"Year": 2015,
"Data": 50
};
var iVals = d3.range(0, 1, 0.2).map(function(d) {
return d3.interpolateObject(a, b)(d)
});
console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>
Alternatively, create a function that returns the interpolator:
var iFunc = function(d) {
return d3.interpolateObject(a, b)(d)
};
var iVals = d3.range(0, 1, 0.2).map(iFunc);
Here is the corresponding demo:
var a = {
"Country": "Ireland",
"Year": 2010,
"Data": 10
};
var b = {
"Country": "Ireland",
"Year": 2015,
"Data": 50
};
var iFunc = function(d) {
return d3.interpolateObject(a, b)(d)
};
var iVals = d3.range(0, 1, 0.2).map(iFunc);
console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>
PS: Not related to your question, but the "stop" value in d3.range() is not inclusive. So, if you want to get the values in the object b, it should be:
d3.range(0, 1.2, 0.2)
Here it is:
var a = {
"Country": "Ireland",
"Year": 2010,
"Data": 10
};
var b = {
"Country": "Ireland",
"Year": 2015,
"Data": 50
};
var iVals = d3.range(0, 1.2, 0.2).map(function(d) {
return d3.interpolateObject(a, b)(d)
});
console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>

Related

how to use reduce with query on Dynamodb scan

I am learning DynamoDB and have difficulties understanding where to perform a reduction on data once a DynamoDB query has provided the results and where in the code it would go.
I would like to sum up the running_time_secs.
async function scanForResults () {
try {
var params = {
TableName: "Movies",
ProjectionExpression: "#yr, title, info.rating, info.running_time_secs, info.genres",
FilterExpression: "#yr between :start_yr and :end_yr",
ExpressionAttributeNames: {
"#yr": "year",
},
ExpressionAttributeValues: {
":start_yr": 1950,
":end_yr": 1985,
}
};
var result = await docClient.scan(params).promise()
console.log(JSON.stringify(result))
//const total = result.reduce((sum, result) => sum + result.info.running_time_secs, 0);
} catch (error) {
console.error(error);
}
}
scanForResults();
Thanks for any help.
EDITED
Maybe you can post the actual value of the result once you resolve it. It would be best to know the actual structure of the result value to better see how you should approach it. You can site the documentation for further reference. It is actually really helpful.
var result = [{
"title": "Piranha",
"year": 1978,
"info": {
"rating": 5.8,
"genres": ["Comedy", "Horror", "Sci-Fi"],
"running_time_secs": 5640
}
}]
var total = result.reduce((sum, result) => sum + result.info.running_time_secs, 0);
console.log(total);
var result2 = [{
"title": "Piranha",
"year": 1978,
"info": {
"rating": 5.8,
"genres": ["Comedy", "Horror", "Sci-Fi"],
"running_time_secs": 5640
}
},
{
"title": "Piranha2",
"year": 1980,
"info": {
"rating": 5.8,
"genres": ["Comedy", "Horror", "Sci-Fi"],
"running_time_secs": 5640
}
}
]
var total2 = result2.reduce((sum, result) => sum + result.info.running_time_secs, 0);
console.log(total2);
var resultObj = {
"title": "Piranha",
"year": 1978,
"info": {
"rating": 5.8,
"genres": ["Comedy", "Horror", "Sci-Fi"],
"running_time_secs": 5640
}
}
var totalObj = resultObj.reduce((sum, result) => sum + result.info.running_time_secs, 0);
console.log(totalObj);
I think base on the error you are receiving, result might not be an array. You can refer to the code snippet I posted. I used your code and just created the result array to mimic the result you get from the scan function. As you can see on the 3rd reduce function used on resultObj, I got the response error you got. reduce is an array method and using it to an object would result in such an error because object does not implement this method. Hence, I think that the result you are getting is not an array.

How to efficiently search and get item from array of objects using for\for each loops?

I have 3 arrays, lets call them, A, B and C. A and B are arrays of strings and C is array of objects.
A = ['Geography1', 'Geography2', 'Geography3', ...];//length will range from 100 to 500.
B = ['Jan 2018', 'Feb 2018', 'Mar 2018' ...];//length will range from 12 to 36.
C = [
{"geoName": "Geography1", "month": "Jan 2018", "sales": 1, "growth": 12.11, "marketShare": 13.11},
{"geoName": "Geography1", "month": "Feb 2018", "sales": 2, "growth": 22.22, "marketShare": 23.22},
....
....
....
{"geoName": "Geography2", "month": "Jan 2018", "sales": 3, "growth": 33.33, "marketShare": 12.4},
{"geoName": "Geography2", "month": "Feb 2018", "sales": 4, "growth": 23.45, "marketShare": 12.4},
....
....
....
....
]; length will be A.length * B.length, approximately it will range from 1,200 to 18,000.
Using those arrays and based on either 'sales' or 'growth', I need to generate another array, say D, which needs to have following format(shown values are for 'sales', if we filter by growth, growth values need to be shown againt each geography\month), using for or for each loop:
D = [
{"geoName":"Geography1", "Jan 2018": 1, "Feb 2018":2,....},
{"geoName":"Geography2", "Jan 2018": 3, "Feb 2018":4,....},
....
...
....
];
Currently this is how my code looks, but it's not efficient for large datasets. It takes long time to execute:
function formatData(){
D = [];
var displayBy = "sales"; //or "growth"
for each(geoDesc in A)
{
var row = {};
row['geoName']= geoDesc;
var val:Number;
for each(var month:String in B)
{
val = getValue(C, month, geoDesc, displayBy)
row[month] = val;
}
D.push(row);
}
//D here will have final output
}
function getValue(source:Array, month:String, geoDesc:String,displayBy:String):Number
{
var value:Number = 0;
var len:int = source.length;
for(var i:int = 0; i < len; i++)
{
var item:Object = source[i];
if(item['month'] == month && item['geoName'] == geoDesc)
{
value = item[displayBy];
break;
}
}
return value;
}

Javascript code to split JSON data into two datapoints array to bind with stackedbar chart canvasjs?

I have variable data having json data as below:
[
{
"BillingMonth":"11",
"BillingYear":"2016",
"Volume":"72",
"BillingMonthName":"November",
"BillingProduct":"Product1"
},
{
"BillingMonth":"11",
"BillingYear":"2016",
"Volume":"617",
"BillingMonthName":"November",
"BillingProduct":"Product2"
},
{
"BillingMonth":"12",
"BillingYear":"2016",
"Volume":"72",
"BillingMonthName":"December",
"BillingProduct":"Product1"
},
{
"BillingMonth":"12",
"BillingYear":"2016",
"Volume":"72",
"BillingMonthName":"December",
"BillingProduct":"Product2"
}
]
What I want to split above json data using javascript/jquery and get them stored in two variables data1, data2 having json data as below as result:
{
type: "stackedBar",
legendText: "Product1",
showInLegend: "true",
data1: [
{ x: November, y: 72 },
{ x: December, y: 72 },
]
}
and
{
type: "stackedBar",
legendText: "Product2",
showInLegend: "true",
data2: [
{ x: November, y: 617 },
{ x: December, y: 72 },
]
}
The above will bind in canvas js stackedbar chart.
Thanks!
Hey here's a solution I had a lot of fun working on I hope it works well for you. I wasn't sure if you would always have 2 products product1, product2 so I went with a more general approach for n amount of products. The result is in an array format, but you can use es6 destructuring to get the two variables data1 and data2 like I did below:
/*
* helper function to restructure json in the desired format
*/
function format(obj) {
var formatted = {
"type": "stackedBar",
"legendText": obj.BillingProduct,
"showInLegend": "true",
"data": [{
"x": obj.BillingMonthName,
"y": obj.Volume
}]
}
return formatted;
}
/*
* returns an array of unique products with corresponding BillingMonth/Volume data
*/
function getStackedBarData(data) {
// transform each obj in orignal array to desired structure
var formattedData = data.map(format);
// remove duplicate products and aggregate the data fields
var stackedBarData =
formattedData.reduce(function(acc, val){
var getProduct = acc.filter(function(item){
return item.legendText == val.legendText
});
if (getProduct.length != 0) {
getProduct[0].data.push(val.data[0]);
return acc;
}
acc.push(val);
return acc;
}, []);
return stackedBarData;
}
var data = [{
"BillingMonth": "11",
"BillingYear": "2016",
"Volume": "72",
"BillingMonthName": "November",
"BillingProduct": "Product1"
}, {
"BillingMonth": "11",
"BillingYear": "2016",
"Volume": "617",
"BillingMonthName": "November",
"BillingProduct": "Product2"
}, {
"BillingMonth": "12",
"BillingYear": "2016",
"Volume": "72",
"BillingMonthName": "December",
"BillingProduct": "Product1"
}, {
"BillingMonth": "12",
"BillingYear": "2016",
"Volume": "72",
"BillingMonthName": "December",
"BillingProduct": "Product2"
}]
var dataVars = getStackedBarData(data);
var data1 = dataVars[0];
var data2 = dataVars[1];
console.log(data1);
console.log(data2);
Hope this helps you!

Converting an array object to another object-Part 2 in javascript

This is a follow up question of Converting an object into array
.Now I would like to do a reverse engineering where I want to convert back the JOSN to the original format except its a object ,like as shown in the example below.
var data1=[
{
"name": "Coal",
"value": "2",
"time": "2015-11-31 00:00:00",
"level":"10"
},
{
"name": "Shale",
"value": "4",
"time": "2015-10-31 00:00:00",
"level":"20"
}
]
to
var data2=
{
"Coal": {
"September 2015": "2",
"level":"10"
},
"Shale": {
"October 2015": "4",
"level":"20"
}
}
where the result is an object not an array.Can anyone pls help me on this issue
function yymmddToString(yymmdd) {
var months = ['January', 'February', 'March', 'April' .....];
var x = yymmdd.split('-');
return months[parseInt(x[1], 10)] + ' ' + x[0];
}
var result = data1.reduce(function(result, datum) {
var x = result[datum.name] = result[datum.name] || {};
x[yymmddToString(datum.time)] = datum.value;
return result;
}, {});

How do I get the combined length of multiple children in a JSON array?

I have the below dictionary in which I want to get the length all notifications (4).
[
{
"name": "3 Bedroom Fixer Upper",
"city": "Santa Rosa",
"id": 1,
"state": "CA",
"date_added": "2/3/14",
"completion_status": "20",
"notifications": [
"Quarterly Checklist",
"Pre-Summer",
"Annual Checklist"
],
"sq_ft": 2200,
"year_built": 1994,
"bedrooms": 3,
"bathrooms": 2.5,
"lot_size": 2.5
},
{
"name": "Ski Cabin",
"city": "Tahoe",
"id": 2,
"state": "CA",
"date_added": "3/3/14",
"completion_status": "45",
"notifications": [
"Quarterly Checklist"
],
"sq_ft": 1950,
"year_built": 1984,
"bedrooms": 3.5,
"bathrooms": 2,
"lot_size": 3
}
];
I am able to get the length of a single objects notifications (example all_properties[0].notifications.length = 3) but all_properties.notifications.length does not return anything. What would the syntax be to return the length of all notifications? (4)
http://jsfiddle.net/dakra/U3pVM/
Sorry if I used the incorrect lingo regarding JSON dictionaries (I am new to them.)
You can use some functional programming with Array.prototype.reduce:
function findSum(arr) {
return arr.reduce(function (a, b) {
return a + b.notifications.length;
}, 0);
}
and call it like findSum(all_properties).
For multiple arrays in array called arrays:
var totalSum = 0;
for (var i = 0; i < arrays.length; i += 1) {
totalSum = findSum(arrays[i]);
}
you can use the Array's reduce function
var data = [...your data array...]
var num = data.reduce(function(a,b){
return a+b.notifications.length;
},0);
console.log("Number of notifications:",num);

Categories

Resources