Javascript - Calculations with properties of an object - javascript

I am currently attempting to calculate the growth percentage change of the properties in an object. I have the calculation that I am able to successfully do with an array of integers, but I am having a hard time applying the same logic to the properties of an object.
My expected outcome is to perform a calculation to data similar to this: year2011-year2010 /year2010; year2012-year2011 /year2011 etc.. in an array such as this:
calculationData = [14.9274 , -0.1875, 0.6122]
Here is an example of a manual way of solving this:
let calc2010 = data.year2011 - data.year2010 / data.year2010
let calc2011 = data.year2011 - data.year2012 / data.year2011
calculationData.push(calc2010, calc2011)
const data = [{'year2010': 4323, 'year2011': 64532, 'year2012': 52432, 'year2013': 84532, 'year2014': 63232, 'year2015': 49332 }]
How can I accomplish this?
In the code snippet below; I provided two examples First, is the properties of objects I am attempting to calculate, and second is showing the calculations working for an array of integers
const data = [{'year2010': 4323, 'year2011': 64532, 'year2012': 52432, 'year2013': 84532, 'year2014': 63232, 'year2015': 49332 }]
let calculationData = data.map(function (stPer, i) {
return 100 * (stPer - data[i - 1]) / (data[i - 1]);
});
console.log(calculationData)
const dataArr = [ [312], [4342], [4343] ]
let calculationDataTwo = dataArr.map(function (stPer, i) {
return 100 * (stPer - dataArr[i - 1]) / (dataArr[i - 1]);
});
console.log(calculationDataTwo)

Here is a solution which converts your object into an array of data in ascending order by year.
const data = {'year2010': 4323, 'year2011': 64532, 'year2012': 52432, 'year2013': 84532, 'year2014': 63232, 'year2015': 49332 };
const calculationData = Object.entries(data).sort(([year1], [year2]) => {
return Number(year1.slice(4)) - Number(year2.slice(4));
}).map(([year, value]) => value);
This is using the Object.entries function to create an array which looks like: [['year2010', 4323], ['year2011', 64532], etc...]
Next, we can sort it and map it easily. Sorting may not be needed if you guarantee the data is pre-sorted, in which case you only need to use map.
The result is an array like this: [4323, 64532, 52432, 84532, 63232, 49332]
You should be able to plug that into your existing code.
EDIT: I see there is still some additional processing to do after you get to this point. Do you need any help after this point?

You need to put each year in a different array element, not all years in the same object.
And when you use map, you need to start with array index 1, because there's no previous element before index 0 to compare with. You can do this by mapping over a slice.
const data = [{
year: 2010,
value: 4323
},
{
year: 2011,
value: 64532
},
{
year: 2012,
value: 52432
},
{
year: 2013,
value: 84532
},
{
year: 2014,
value: 63232
},
{
year: 2015,
value: 49332
}
]
let calculationData = data.slice(1).map(function(stPer, i) {
return 100 * (stPer.value - data[i].value) / data[i].value;
});
console.log(calculationData)

Related

Traversing an array of objects and adding to other array based on condition

I have an array containing the following objects.
var notTotal = [{"Year":2012,"Value":800579},
{"Year":2012,"Value":654090},
{"Year":2012,"Value":758092},
{"Year":2013,"Value":343928},...More objects.. ]
What im trying to do is traverse this array of objects where only one Year exists instead of multiple and to add up the Values for that year. Using the example above..
var total = [{"Year":2012,"Value":2556689},
//Total of first three 2012 assuming there isnt anymore 2012 in the array
{"Year":2013,"Value":343928},...]
I have tried something like the following:
for(var i = 0; i < notTotal.length; i++) {
if (total.includes(notTotal[i].Year || notTotal[i])) {
//Add the value of the year in notTotal array to the same year in total array
} else {
total.push(notTotal[i]); //add year and value if it does not exist to the total array
}
}
Apologies if this is a duplicate. It seems like a pretty specific question.
Thanks!
An easy solution would be to create an object, holding totals by year.
var totalByYear = {};
You can then loop over the array using notTotal.forEach or a for loop as you've shown, adding to the value of the relevant year inside the totalByYear object.
notTotal.forEach(function(ele) { totalByYear[ele.Year] += ele.Value });
This yields an object with year keys and total values, e.g. using your example:
{'2012': 2556689, '2013': 343928 /* other years */}
The desired format (for D3) can then be built from the totalByYear object (and the totals by year printed):
var totals = [];
for (year in totalByYear) {
console.log('Total for year ' + year + ' is ' + totalByYear[year]);
//Build the correctly formatted array
totals.push({ Year: year, Value: totalByYear[year]});
}
//Prints:
//Total for year 2012 is 2556689
//etc.etc.
The totals array will then have the desired format.
Great question! To explain what's happening in your if (total.includes(notTotal[i].Year || notTotal[i])) is that you are looking through your total array for either just the year, or just an existing notTotal[i] exactly as it is. So your loop is trying to find a value that's exactly 2012 or exactly "Year":2012,"Value":2556689. Ergo, if your total array looked like this:
[{"Year":2012, "Value": 12345}]
your for loop would not find it even though there is an object with 2012 as its year. As for how to fix this, take a look at this previous question!
How to determine if Javascript array contains an object with an attribute that equals a given value?
Hopefully that helps :)
var notTotal = [{"Year":2012,"Value":800579},
{"Year":2012,"Value":654090},
{"Year":2012,"Value":758092},
{"Year":2013,"Value":343928}]
var totalObj = notTotal.reduce((sum, obj) => {
sum[obj.Year] = sum[obj.Year] + obj.Value || obj.Value;
return sum;
}, {});
// convert total to the format you need;
var total = Object.entries(totalObj).map(([Year, Value]) => ({Year, Value}))
console.log(total);
one more solution :
function yearlyValueFilter(array){
var yearlyValue = {}
array.forEach( (obj) => { //litterate on the input
var year = obj.Year
var value = obj.Value
if((year in yearlyValue)){ //if the array with not duplicated years conatins the litteration year just plus that value
yearlyValue[year] += value
}else{ //if not conatins, it gets as a basic value
yearlyValue[year] = value
}
})
return yearlyValue
}
You could built a hash table:
var hash = {},total=[];
for(const {Year,Value} of notTotal){
if(hash[Year]){
hash[Year].Value+=Value;
}else{
total.push(hash[Year]={Year,Value});
}
}
In action
Note: object properties are normally not capitalized...
You could use a hash table and check if the year does not exist, then generate a new result set. Then update the total count.
var values = [{ Year: 2012, Value: 800579 }, { Year: 2012, Value: 654090 }, { Year: 2012, Value: 758092 }, { Year: 2013, Value: 343928 }],
hash = Object.create(null),
totals = [];
values.forEach(function (o) {
hash[o.Year] || totals.push(hash[o.Year] = { Year: o.Year, Value: 0 });
hash[o.Year].Value += o.Value;
});
console.log(totals);

How to find the position of all array items from a loop

I'm brand new to programming so I apologize if this is a simple question.
I had a unique practice problem that I'm not quite sure how to solve:
I'm dealing with two arrays, both arrays are pulled from HTML elements on the page, one array is representing a bunch of states, and the next array is representing their populations. The point of the problem is to print the name of the states and their less than average populations.
To find and print all of the populations that are less than the average I used this code:
function code6() {
// clears screen.
clr();
// both variables pull data from HTML elements with functions.
var pop = getData2();
var states = getData();
var sum = 0;
for( var i = 0; i < pop.length; i++ ){
sum += parseInt( pop[i], 10 );
var avg = sum/pop.length;
if (pop[i] < avg) {
println(pop[i]);
// other functions used in the code to get data, print, and clear the screen.
function getData() {
var dataSource = getElement("states");
var numberArray = dataSource.value.split('\n');
// Nothing to split returns ['']
if (numberArray[0].length > 0) {
return(numberArray);
} else {
return [];
}
}
// Get the data from second data column
function getData2() {
var dataSource = getElement("pops");
var numberArray = dataSource.value.split('\n');
// Nothing to split returns ['']
if (numberArray[0].length > 0) {
return(numberArray);
} else {
return [];
}
}
// Clear the 'output' text area
function clr() {
var out = getElement("output");
out.value = "";
}
// Print to the 'output' HTML element and ADDS the line break
function println(x) {
if (arguments.length === 0) x = '';
print(x + '\n');
}
Now I just need to know how to get the value of these positions within the array so I can pull out the same positions from my states array and display them both side by side. Both arrays have the identical amount of items.
I hope this makes sense and thanks in advance to anyone who has time to take a look at this.
Best regards,
-E
Its a little hard to tell what you are trying to accomplish, but I guess you are going for something like:
'use strict'
function code6() {
const populations = ['39000000', '28000000', '21000000'];
const stateNames = ['california', 'texas', 'florida'];
const states = populations.map((population, i) => ({
'name': stateNames[i],
'population': Number(population),
}));
const sum = states.reduce((sum, state) => sum + state.population, 0);
const average = sum / populations.length;
states
.filter(state => state.population < average)
.forEach(state => {
const name = state.name;
const population = state.population;
console.log(`state name: ${name}, population: ${population}`);
});
}
// run the code
code6();
// state name: texas, population: 28000000
// state name: florida, population: 21000000
I took the liberty of refactoring your code to be a little more modern (es6) and Idiomatic. I hope its not to confusing for you. Feel free to ask any questions about it.
In short you should use:
'use strict' at the top of your files
const/let
use map/filter/forEach/reduce to iterate lists.
use meaningfull names
, and you should avoid:
classic indexed for-loop
parseInt
, and pretty much never ever use:
var
If your states array is built with corresponding indices to your pop one, like this:
states; //=> ['Alabama', 'Alaska', 'Arizona', ...]
pop; //=> [4863300, 741894, 6931071, ...]
then you could simply update your print statement to take that into account:
if (pop[i] < avg) {
println(state[i] + ': ' + pop[i]);
}
Or some such.
However, working with shared indices can be a very fragile way to use data. Could you rethink your getData and getData2 functions and combine them into one that returns a structure more like this the following?
states; //=> [
// {name: 'Alabama', pop: 4863300}
// {name: 'Alaska', pop: 741894},
// {name: 'Arizona', pop: 6931071},
// ...]
This would entail changes to the code above to work with the pop property of these objects, but it's probably more robust.
If your pop and state looks like:
var state = ['state1', 'state2', ...];
var pop = ['state1 pop', 'state2 pop', ...];
Then first of all, avg is already wrong. sum's value is running along with the loop turning avg's formula into sum as of iteration / array length instead of sum of all pops / array length. You should calculate the average beforehand. array.reduce will be your friend.
var average = pop.reduce(function(sum, val){return sum + val;}, 0) / pop.length;
Now for your filter operation, you can:
Zip up both arrays to one array using array.map.
Filter the resulting array with array.filter.
Finally, loop through the resulting array using array.forEach
Here's sample code:
var states = ['Alabama', 'Alaska'];
var pop = [4863300, 741894];
var average = pop.reduce(function(sum, val){return sum + val;}) / pop.length;
console.log('Average: ' + average);
states.map(function(state, index) {
// Convert 2 arrays to an array of objects representing state info
return { name: state, population: pop[index] };
}).filter(function(stateInfo) {
console.log(stateInfo);
// Filter each item by returning true on items you want to include
return stateInfo.population < average;
}).forEach(function(stateInfo) {
// Lastly, loop through your results
console.log(stateInfo.name + ' has ' + stateInfo.population + ' people');
});

A better way than chaining _.groupBy?

When I do this I get an array which stores the data as grouped by the month and day of the date but not by the year (I am doing this to get maximum, minimum, and average values for each day there is data for)
The problem is that the array stores an array of 2-3 values for that day and month within the date which is the key value. Those 2-3 indices each have an array of length one that holds a reference to an object which has the actual data point (level) I need. The object contains three attributes, date, id (which is always null), and level which is a float.
I either need to find a way so those 2-3 indices hold the object directly, or find a way that _.each can access the level.
Any thoughts?
var groupedData = _.groupBy(data, "date");
var groupedLevels = _.groupBy(groupedData, function (points, date) {
var dateParsed = parseDate(date);
var month = dateParsed.getMonth();
var day = dateParsed.getDate();
var monthDay = month + "-" + day;
return monthDay;
});
_.each(groupedLevels, function (points, date) {
var levels = _.map(_.pluck(points, "level"), parseFloat);
minimum.push([ date, R.min(levels) ]);
maximum.push([ date, R.max(levels);
var averageLevel = R.sum(levels) / levels.length;
average.push([date, averageLevel]);
})
So the data, as is, which is the original input looks like this (a sample piece):
[ { date: "2009-01-01",
id: null,
level: "0.08",
},
// ...
]
Currently, groupedData is this:
{ "2009-01-01":
[ { date: "2009-01-01",
id: null,
level: "0.08"
}
],
// ...
}
groupedLevels looks like this, for example:
{ "0-1":
[ [ { date: "2009-01-01".
id: null,
level: "0.08"
}
],
// ...
],
// ...
}
I want to skip having all the arrays of length one and just have the object stored there.
I think you can fix the immediate issue by replacing this line:
var levels = _.map(_.pluck(points, "level"), parseFloat);
With this:
var levels = _.map(_.pluck(points[0], "level"), parseFloat);
...but I think the real problem might be that you're doing groupBy twice when you don't need to. This single groupBy ought to be equivalent, but without the extra nested array:
var groupedLevels = _.groupBy(data, function(item) {
var dateParsed = parseDate(item.date);
var month = dateParsed.getMonth();
var day = dateParsed.getDate();
return month + '-' + day;
});
With this, your each should work as expected.

Grouping / counting in javascript using underscore.js

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
}]

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