I've 18 documents in my collection movie. For each movie for example:
{
title: "Test Movie 2",
date: [20130808, 20130606],
score: [ {"pete": 1, "mary": 1, "simon": 1, "pat": 2, "mike": 0},
{"pete": 5, "mary": 5, "simon": 5, "pat": 0, "mike": 5}]
}
Now, I want to show the date and sum of the second document in the array 'score' on the client, like:
<div class="details">
Test Movie 2: 20 points 20130606
</div>
Have somebody an idea how I can do that?
You could use a transform, it might be better to define each document with a name and explicitly defining the points as name/value pair instead of points being the value for each persons name.
But this should work:
Movies.find({}, {transform: function(doc) {
var total_points = 0;
var people = doc.score[1]; //Second in array
for(point in people) {
total_points += people[point]
}
doc.points = total_points;
return doc;
}});
This should give you:
{
title: "Test Movie 2",
points: 20,
date: [20130808, 20130606],
score: [ {"pete": 1, "mary": 1, "simon": 1, "pat": 2, "mike": 0},
{"pete": 5, "mary": 5, "simon": 5, "pat": 0, "mike": 5}]
}
Mongo can likely do this outright, but you're not going to be able to do this directly by querying a collection due to limitation of the Mongo livedata package as of 0.6.5. Aggregation framework is also off-limits, and they seem to have pulled the 'hidden' method that allowed direct access to Mongo.
So your options are:
Use transform as per Akshat's answer - the best of both worlds
Aggregate manually in the client in a template helper. I recommend using _.js which comes 'free' with Meteor (again this might change, but you could always pull the library in manually later).
var sum = _.reduce(score[1], function(memo, num){ return memo + num; }, 0);
(I didn't test the above, but it should send you on the right track).
Aggregate upstream, during the insert/update/deletes, likely by observing changes on the collection and 'feeding in' the sum() of the elements you are inserting in either the same collection or an aggregate one.
Which method you use depends on where performance matters most to you, usually doing aggregates before you insert tend to avoid issues later on, and lightens load.
Good luck and let us know how you get on.
Related
I want to analyse and extract the total annual deforestation between 2001-2021 using the Hansen forest change dataset in Google Earth Engine.
For that, I use a feature collection containing (let's say) sub-national boundaries. The code has referred to as Shape, which has about 8-9k polygons.
The code that analyses annual deforestation (area in km2) for each shape (subset of Feature Collection Shapes) and saves it as an array is pasted below (this part works fine):
var annualforestloss = function(ID)
{
// Get a feature collection with just the custom shapefile feature.
// Shape contains multiple features (polygons)
// Use any shapefile, if mine doesn't work
var geometry = Shape.filter(ee.Filter.eq('ID', ID));
//print(geometry);
Map.addLayer(geometry);
// Get the loss image.
// This dataset is updated yearly, so check for the latest version.
var gfc2021 = ee.Image('UMD/hansen/global_forest_change_2021_v1_9');
var lossImage = gfc2021.select(['loss']);
//Converting m2 to km2
var lossAreaImage = lossImage.multiply(ee.Image.pixelArea()).divide(1000000);
var lossYear = gfc2021.select(['lossyear']);
var lossByYear = lossAreaImage.addBands(lossYear).reduceRegion({
reducer: ee.Reducer.sum().group({
groupField: 1
}),
geometry: geometry,
scale: 30,
maxPixels: 1e9
});
//print(lossByYear);
var statsFormatted = ee.List(lossByYear.get('groups'))
.map(function(el) {
var d = ee.Dictionary(el);
return [ee.Number(d.get('group')).format("20%02d"), d.get('sum')];
});
var statsDictionary = ee.Dictionary(statsFormatted.flatten());
//print(statsFormatted)
//print(statsDictionary)
// Since there has been no forest loss for some years, the code above does not include those years.
// To remedy that, we use another dictionary with values as zero and later combine them without overlap
var dict1 = ee.Dictionary({
"2001": 0, "2002": 0, "2003": 0, "2004": 0, "2005": 0, "2006": 0, "2007": 0, "2008": 0,
"2009": 0, "2010": 0, "2011": 0, "2012": 0, "2013": 0, "2014": 0, "2015": 0, "2016": 0,
"2017": 0, "2018": 0, "2019": 0, "2020": 0, "2021": 0,});
statsDictionary = statsDictionary.combine(dict1, false);
//print(statsDictionary)
// Platting chart
/*
var chart = ui.Chart.array.values({
array: (statsDictionary.values()),
axis: 0,
xLabels: statsDictionary.keys()
}).setChartType('LineChart')
.setOptions({
title: 'Yearly Forest Loss (ID-'+ID+')',
hAxis: {title: 'Year', format: '####'},
vAxis: {title: 'Area (square kilometer)'},
legend: { position: "none" },
lineWidth: 1,
pointSize: 3
});
print(chart)
*/
// Adding 'ID' for seamless data analysis in python
return statsDictionary.values().add(ID)
};
However, the problem I am facing is: Looping through the features in Shapes: Although I know that 'for loops' are not recommended, I am limited by my ability to use map() function in GEE, which returns some client/server error.
// This function combines results from arrays in a loop
var combinearray = function(array1, array2)
{
var arraynew = (ee.Array.cat([array1, array2], 0))
return arraynew
}
// Size of the features (total shapes about 8664)
print(Shape.size())
//**Limited by skill to run it with 'map' function. ########## Please help!**!!!!!!!############
// Works fine for less loops. Hangs for double digit loops
var list = (annualforestloss(0))
for (var ID = 1; ID <= 6; ID++) {
var listnew = (annualforestloss(ID))
var list = (combinearray(list, listnew))
}
print(list)
//reshaping the array for export
var combinedarray = (list.reshape([7,22]))
print('--')
print(combinedarray)
Any help is greatly appreciated.
The following link can be used to see the code editor: https://code.earthengine.google.com/cd335cd1356dc69e3c9adc4ba9710bcb
I think using ReduceRegions() should do the trick. Here is a sample code (https://code.earthengine.google.com/?scriptPath=users%2Fdvps95%2Fdefault%3AHansen_Annual_ForestLoss)
The reduceRegion() maps the functions across multiple polygons. I hope this answers your question. I would suggest you follow the tutorials from "Spatial thoughts"- Ujwal gandhi does an excellent job in breaking down the concepts.
Good luck!
i struggle my had now sine several days with a way to do conditional filter of an object array with another object array.
lack on capabilities to properly abstract here... maybe you ahve some ideas.
I have a given Object Array A but more complex
var ArrA = [{
number: 1,
name: "A"
}, {
number: 2,
name: "C"
}]
And i want to filer for all results matiching id of Object Array B
var ArrB = [{
id: 1,
categorie: "wine"
}, {
id: 3,
categorie: "beer"
}, {
id: 10,
categorie: "juice"
}]
And in the best case moving this directly also together with an if condition.... but i was not able to handle it ... here is where i am now ... which is not working....
let newArray = ArrA.filter{$0.number == ArrB.... }.
if (newArray.count != 0){
// Do something
}
is there a lean way to compare one attribute of every object in an array with one attribute of another every object in an array ?
Lets break this down: You need all arrA objects that matches arrB ids, so first thing first you need to map your arrB to a list of id (because you dont need the other infos)
let arrBid = Set(arrB.map({ $0.id })) // [1, 3, 10]
As commented below, casting it to Set will give you better results for huge arrays but is not mandatory though
Then you just need to filter your first arrA by only keeping object that id is contained into arrBid :
let arrAFilter = arrA.filter({ arrBid.contains($0.number) })
[(number: 1, name: "A")]
and voila
I am working on react js application and building comment reply structure. API is returning me an array of comments, but it's not in a comment hierarchy.
My API response is like this:
review: {_id: 35,email: "test#gmail.com", review: "Shavon B does an AMAZING job!! I had another fant…e taking care of my home. Shavon is a rock star!"}
comments: [
0: {_id: 36, status: 1, email: "neha#shandil.com", review: "Shavon B does an AMAZING job!! I had another fant…e taking care of my home. Shavon is a rock star!", parent_id: 35, reply_to:35}
1: {_id: 37, status: 1, email: "archana#gmail.com", review: " Thank you for sharing your review of your home cl…e taking care of my home. Shavon is a rock star!", parent_id: 35, reply_to:36}
2: {_id: 39, status: 1, email: "radhika#dummy-url.com", review: "Shavon B does an AMAZING job!! I had another fant…e taking care of my home. Shavon is a rock star!", parent_id: 35, reply_to:37}
3: {_id: 40, status: 1, email: "archi#123.com", review: "good", parent_id: 35, reply_to:36}
4: {_id: 41, status: 1, email: "test#test.com", review: "Testing", parent_id: 35, reply_to:35}
]
here parent_id means these are comments for any blog with id 35, and reply_to means this is a reply for that particular comment _id, like array at index 1 is a reply for comment at 0 index.
Now I am also getting a new reply at the end of the list. Now I want to show all comments in their hierarchy.
Now the problem is I am getting a simple array with all comments and replies, how can I show them in the hierarchy.
Is this possible to push HTML in between, please suggest me a solution, I want to show comments up to two levels.
You will need to convert comments to tree structure and will need to write recursive logic to process comments.
Function for converting flat list to the tree:
function unflatten(arr) {
var tree = [],
mappedArr = {},
arrElem,
mappedElem;
// First map the nodes of the array to an object -> create a hash table.
for(var i = 0, len = arr.length; i < len; i++) {
arrElem = arr[i];
mappedArr[arrElem._id] = arrElem;
mappedArr[arrElem._id]['children'] = [];
}
for (var id in mappedArr) {
if (mappedArr.hasOwnProperty(id)) {
mappedElem = mappedArr[id];
// If the element is not at the root level, add it to its parent array of children.
if (mappedElem.parent_id) {
mappedArr[mappedElem['parent_id']]['children'].push(mappedElem);
}
// If the element is at the root level, add it to first level elements array.
else {
tree.push(mappedElem);
}
}
}
return tree;
}
Here is working POC of Recursive Component and tree data in action:
https://stackblitz.com/edit/react-7jhe22?file=index.js
The POC shows automatically adding a random comment to mimic the behavior of user adding a comment.
This also shows how you can append at the end of comments array and still generate comment view with help of unflatten function. Since this is recursive, you can reply to any comment!!
I have an array of objects that I'd like to group by field1 and sum by field2. An example would be a class product that has a title field and a price field.
In an array of products, I have multiple gloves with different prices, and multiple hats with different prices. I'd like to have an array with distinct titles, that aggregate all the prices under the same title.
There's an obvious solution with iterating over the array and using a hash, but I was wondering if there was a "ruby way" of doing something like this? I've seen a lot of examples where Ruby has some unique functionality that applies well to certain scenarios and being a Ruby newbie I'm curious about this.
Thanks
There's a method transform_values added in ruby 2.4 or if you require 'active_support/all', with this you can do something like so:
products = [
{type: "hat", price: 1, name: "fedora"},
{type: "hat", price: 2, name: "sombrero"},
{type: "glove", price: 3, name: "mitten"},
{type: "glove", price: 4, name: "wool"}
]
result = products
.group_by { |product| product[:type] }
.transform_values { |vals| vals.sum { |val| val[:price] } }
# => {"hat"=>3, "glove"=>7}
It's a little unclear to me from the question as asked what your data looks like, so I ended up with this:
Product = Struct.new(:title, :price)
products = [
Product.new("big hat", 1),
Product.new("big hat", 2),
Product.new("small hat", 3),
Product.new("small hat", 4),
Product.new("mens glove", 8),
Product.new("mens glove", 9),
Product.new("kids glove", 1),
Product.new("kids glove", 2)
]
Given that data, this is how I'd go about building a data structure which contains the sum of all the prices for a given title:
sum_by_title = products.inject({}) do |sums, product|
if sums[product.title]
sums[product.title] += product.price
else
sums[product.title] = product.price
end
sums
end
This produces:
{"big hat"=>3, "small hat"=>7, "mens glove"=>17, "kids glove"=>3}
To explain:
Ruby inject takes an initial value and passes that to the iteration block as a "memo". Here, {} is the initial value. The return value from the block is passed into the next iteration as the memo.
The product.title is used as a hash key and the running sum is stored in the hash value. An if statement is necessary because the first time a product title is encountered, the stored value for that title is nil and cannot be incremented.
I probably wouldn't ship this code due to the hidden magic of the hash default value constructor but it's possible to write the same code without the if statement:
sum_by_title = products.inject(Hash.new { 0 }) do |sums, product|
sums[product.title] += product.price
sums
end
Hope you enjoy Ruby as much as I do!
As i understand it, a typical JSON array could be something like this:
{ "measurements":[ {"order":1, "time":"23:55:12.234", "value":3.4543235},
{"order":2, "time":"23:55:14.118", "value":3.9785742},
{"order":3, "time":"23:55:15.942", "value":3.6892639}]
}
But since i have several hundred measurements, i would like to remove the repeating keys like this:
1:
{ "measurements":[ 1, "23:55:12.234", 3.4543235,
2, "23:55:14.118", 3.9785742,
3, "23:55:15.942", 3.6892639]
}
And if i do it like that i also would like to keep the keys for reference - to allow other sequences and maybe even additional information in the array, i could imagine something like this:
2:
{ "measurements":{"assembly":"order,time,value",
"values": [ 1, "23:55:12.234", 3.4543235,
2, "23:55:14.118", 3.9785742,
3, "23:55:15.942", 3.6892639]
}
}
Are 1 and or 2 correct JSON and if not is there another way?
And even if that is allowed - is there a more sensible / common way of doing this?