Group by in javascript or angularjs - javascript

I just want to do sum value column based on the Year and my data is below, but I don't know how to do this either using angular(in script) or javascript.
[
{"Year":2013,"Product":"A","Value":0},
{"Year":2013,"Product":"B","Value":20},
{"Year":2013,"Product":"A","Value":50},
{"Year":2014,"Product":"D","Value":55},
{"Year":2014,"Product":"M","Value":23},
{"Year":2015,"Product":"D","Value":73},
{"Year":2015,"Product":"A","Value":52},
{"Year":2016,"Product":"B","Value":65},
{"Year":2016,"Product":"A","Value":88}
]
I want to perform the sum on Value column and remove Product column as well.
Thanks

Edit As commenters have pointed out, this doesn't even require Lodash. Been using Lodash so much for current project I forgot reduce is built in. :-)
Also updated to put data in desired form [{"yyyy" : xxxx},...].
This code will accomplish this:
var data = [{"Year":2013,"Product":"A","Value":0},{"Year":2013,"Product":"B","Value":20},{"Year":2013,"Product":"A","Value":50},{"Year":2014,"Product":"D","Value":55},{"Year":2014,"Product":"M","Value":23},{"Year":2015,"Product":"D","Value":73},{"Year":2015,"Product":"A","Value":52},{"Year":2016,"Product":"B","Value":65},{"Year":2016,"Product":"A","Value":88}];
var sum = data.reduce(function(res, product){
if(!(product.Year in res)){
res[product.Year] = product.Value;
}else{
res[product.Year] += product.Value;
}
return res;
}, {});
result = [];
for(year in sum){
var tmp = {};
tmp[year] = sum[year];
result.push(tmp);
}
console.log(result);
RESULT:
[{"2013" : 70}, {"2014" : 78}, {"2015" : 125}, {"2016" : 153}]
ORIGINAL ANSWER
The easiest way I can think of to do this is with the Lodash Library. It gives you some nice functional programming abilities like reduce, which basically applies a function to each element of an array or collection one by one and accumulates the result.
In this case, if you use Lodash, you can accomplish this as follows:
var data = [{"Year":2013,"Product":"A","Value":0},{"Year":2013,"Product":"B","Value":20},{"Year":2013,"Product":"A","Value":50},{"Year":2014,"Product":"D","Value":55},{"Year":2014,"Product":"M","Value":23},{"Year":2015,"Product":"D","Value":73},{"Year":2015,"Product":"A","Value":52},{"Year":2016,"Product":"B","Value":65},{"Year":2016,"Product":"A","Value":88}];
result = _.reduce(data, function(res, product){
if(!(product.Year in res)){
res[product.Year] = product.Value;
}else{
res[product.Year] += product.Value;
}
return res;
}, {});
This yields:
{
"2013": 70,
"2014": 78,
"2015": 125,
"2016": 153
}
Basically, what we're telling Lodash is that we want to go through all the elements in data one by one, performing some operation on each of them. We're going to save the results of this operation in a variable called res. Initially, res is just an empty object because we haven't done anything. As Lodash looks at each element, it checks if that Product's year is in res. If it is, we just add the Value to that year in res. If it's not, we set that Year in res to the Value of the current product. This way we add up all the product values for each year.
If you want to try it out you can do it here:
Online Lodash Tester
Cheers!

You could do this using plain JavaScript. We use an object that will hold the results and the forEach array method. The object that would hold the results would have as keys the years and as values the sums of the corresponding values.
var data = [
{"Year":2013,"Product":"A","Value":0},
{"Year":2013,"Product":"B","Value":20},
{"Year":2013,"Product":"A","Value":50},
{"Year":2014,"Product":"D","Value":55},
{"Year":2014,"Product":"M","Value":23},
{"Year":2015,"Product":"D","Value":73},
{"Year":2015,"Product":"A","Value":52},
{"Year":2016,"Product":"B","Value":65},
{"Year":2016,"Product":"A","Value":88}
];
groupedData = {};
data.forEach(function(item){
var year = item.Year;
var value = item.Value;
if(groupedData.hasOwnProperty(year)){
groupedData[year]+=value;
}else{
groupedData[year]=value;
}
});
console.log(groupedData);

Related

Get "leaderboard" of list of numbers

I am trying to get a kind of "leaderboard" from a list of numbers. I was thinking of making an array with all the numbers like this
var array = [];
for (a = 0; a < Object.keys(wallets.data).length; a++) { //var wallets = a JSON (parsed) response code from an API.
if (wallets.data[a].balance.amount > 0) {
array.push(wallets.data[a].balance.amount)
}
}
//Add some magic code here that sorts the array into descending numbers
This is a great option, however I need some other values to come with the numbers (one string). That's why I figured JSON would be a better option than an array.
I just have no idea how I would implement this.
I would like to get a json like this:
[
[
"ETH":
{
"balance":315
}
],
[
"BTC":
{
"balance":654
}
],
[
"LTC":
{
"balance":20
}
]
]
And then afterwards being able to call them sorted descending by balance something like this:
var jsonarray[0].balance = Highest number (654)
var jsonarray[1].balance = Second highest number (315)
var jsonarray[2].balance = Third highest number (20)
If any of you could help me out or point me in the right direction I would appreciate it greatly.
PS: I need this to happen in RAW JS without any html or libraries.
You should sort the objects before making them a JSON. You can write your own function or use a lambda. See this [https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value]
Since you are dealing with cryptocurrency you can use the currency-code as a unique identifier.
Instead of an array, you can define an object with the currency as properties like this:
const coins = {
ETH: [300, 200, 500],
BTC: [20000, 15000, 17000]
}
then you can access each one and use Math.max or Math.min to grab the highest / lowest value of that hashmap. E.G. Math.max(coins.BTC)
And if you need to iterate over the coins you have Object.keys:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Thank you all for your answer. I ended up using something like:
leaderboard = []
for (a = 0; a < Object.keys(wallets.data).length; a++) {
if (wallets.data[a].balance.amount > 0) {
leaderboard.push({"currency":wallets.data[a].balance.currency, "price":accprice}) //accprice = variable which contains the value of the userhold coins of the current coin in EUR
}
}
console.log(leaderboard.sort(sort_by('price', true, parseInt)));

Javascript ForEach on Array of Arrays

I am looping through a collection of blog posts to firstly push the username and ID of the blog author to a new array of arrays, and then secondly, count the number of blogs from each author. The code below achieves this; however, in the new array, the username and author ID are no longer separate items in the array, but seem to be concatenated into a single string. I need to retain them as separate items as I need to use both separately; how can I amend the result to achieve this?
var countAuthors = [];
blogAuthors = await Blog.find().populate('authors');
blogAuthors.forEach(function(blogAuthor){
countAuthors.push([blogAuthor.author.username, blogAuthor.author.id]);
})
console.log(countAuthors);
// Outputs as separate array items, as expected:
// [ 'author1', 5d7eed028c298b424b3fb5f1 ],
// [ 'author2', 5dd8aa254d74b30017dbfdd3 ],
var result = {};
countAuthors.forEach(function(x) {
result[x] = (result[x] || 0) + 1;
});
console.log(result);
// Username and author ID become a single string and cannot be accessed as separate array items
// 'author1,5d7eed028c298b424b3fb5f1': 15,
// 'author2,5dd8aa254d74b30017dbfdd3': 2,
Update:
Maybe I can explain a bit further WHY on what to do this. What I am aiming for is a table which displays the blog author's name alongside the number of blogs they have written. However, I also want the author name to link to their profile page, which requires the blogAuthor.author.id to do so. Hence, I need to still be able to access the author username and ID separately after executing the count. Thanks
You could use String.split().
For example:
let result = 'author1,5d7eed028c298b424b3fb5f1'.split(',')
would set result to:
['author1' , '5d7eed028c298b424b3fb5f1']
You can then access them individually like:
result[1] //'5d7eed028c298b424b3fb5f1'
Your issue is that you weren't splitting the x up in the foreach callback, and so the whole array was being converted to a string and being used as the key when inserting into the results object.
You can use array destructuring to split the author name and blog id, and use them to optionally adding a new entry to the result object, and then update that result.
countAuthors = [
['author1', 'bookId1'],
['author2', 'bookId2'],
['author1', 'bookId3'],
['author1', 'bookId4'],
['author2', 'bookId5']
]
var result = {};
countAuthors.forEach(([author, id]) => {
if (result[author] === undefined) {
result[author] = {count: 0, blogIds: []};
}
result[author].count += 1;
result[author].blogIds.push(id);
});
console.log(result);

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');
});

How to push object to array in angularjs?

This is my code
$scope.studentDetails=[];
$scope.studentIds={};
$scope.studentIds[0]{"id":"101"}
$scope.studentIds[1]{"id":"102"}
$scope.studentIds[2]{"id":"103"}
in the above code when i select student id:101 i got marks from services like
$scope.studentMarks={};
$scope.studentMarks[0]{"marks":"67"}
$scope.studentMarks[1]{"marks":"34"}
next i select student id:102 i got marks from services like
$scope.studentMarks={};
$scope.studentMarks[0]{"marks":"98"}
$scope.studentMarks[1]{"marks":"85"}
finally i want to store student details in to one array like
$scope.studentDetails=[{"id":"101","marks":[67,34]},{"id":"102","marks":[98,85]}]
using angularjs.
Seems like its more of a JS question than angular.
What about the Javascript push method?
$scope.studentDetails.push({id: 101, marks: [67, 34]});
You can use Array.push to add one object, or concat, to concat array into another array. See the references.
angularJS is just a library to extend Javascript. You push into an array just like you would any object in Javascript.
First off, you need to declare an array.
$scope.studentIds = []; // Array of student ids.
Then when you want to add, you push:
$scope.studentIds.push({id: "101"});
To do this naively you need to loop through the student ids and then loop through the marks object and adding it to your studentDetails object if the ids match:
var studentDetails = [];
for (var id in studentIds) {
var studentDetail = {}; // this will be a single student
var marks = [];
if (studentIds.hasOwnProperty(id)) {
for (var mark in studentMarks) {
if (studentMarks.hasOwnProperty(mark) && mark.id === id) {
studentDetail.id = id;
marks.push(mark.marks);
}
}
studentDetail.marks = marks;
}
studentDetails.push(studentDetail);
}
$scope.studentDetails = studentDetails;

JavaScript database correlation

I've been trying to 'correlate' between user picked answers and an object property name so that if the two matches then it will display what is inside.
My program is a recipe finder that gives back a recipe that consists of the ingredients the user picked.
my code currently looks like:
//property are the ingredients and the value are the recipes that contain those ingredients. The map is automatically generated
``var map = {
"pork" : [recipe1, recipe2, ...],
"beef" : [],
"chicken" :[],
}
//this gets the user pick from the dom
var cucumber = specificVegetable[7];
var lemon = specificFruits[0];
//Then this code finds the intersection of the recipe(recipes that use more than one ingredients)
function intersect(array1, array2)
{
return array1.filter(function(n) {
return array2.indexOf(n) != -1
});
}
var recipiesWithLemon = map["lemon"]; **// makes the lemon object is map**
var recipiesWithCucumber = map["cucumber"]; **// makes the cucumber object in map**
//Here is where I am stuck
function check(){
var both = intersect(recipiesWithLemon, recipiesWithCucumber);
if ( cucumber.checked && lemon.checked){
for (var stuff in map){
if(stuff="cucumber" && stuff="lemon"){
return both;
}
}
}
}
check();
so basically what I tried to do was I made my intersect and then if user pick is lemon and cucumber then look at the properties in the map object. if the name of the property equals to the exact string then return both. That was the plan but the code does not work and I'm not sure how to fix it.
My plan is to write code for every possible outcome the user may makes so I need to find the correlation between the user pick and the map which stores the recipe. I realize this is not the most effective way but I'm stumped on how to do it another way.
Thanks for the help.
Im using the open source project jinqJs to simplify the process.
I also changed your map to an array of JSON objects. If you must have the map object not as an array, let me know. I will change the sample code.
var map = [
{"pork" : ['recipe1', 'recipe2']},
{"beef" : ['recipe3', 'recipe4']},
{"peach" :['recipe5', 'recipe6']},
{"carrot" :['recipe7', 'recipe8']}
];
var selectedFruit = 'peach';
var selectedVeggie = 'carrot';
var selections = [selectedFruit, selectedVeggie];
var result = jinqJs().from(map).where(function(row){
for(var f in row) {
if (selections.indexOf(f) > -1)
return true;
}
return false;
}).select();
document.body.innerHTML += '<pre>' + JSON.stringify(result, null, 2) + '</pre><br><br>';
<script src="https://rawgit.com/fordth/jinqJs/master/jinqjs.js"></script>

Categories

Resources