I'm noticing significant code duplication while building my first Meteor project, and I'm wondering if there's a way to DRY it up.
My database model has stores, each of which have a number of products, and a field with the amount currently in inventory.
var store_id = Store.insert({name: 'Store 1', max_items: 50});
var p1 = Product.insert({name: 'General', store_id: store_id, item_count: 20});
var p2 = Product.insert({name: 'Special', store_id: store_id, item_count: 10});
I have a template to display a store, and statistics on how many products and items it has.
<template name="store">
<div class="store">
<b>{{name}}</b>
<p>Current items: {{current_items}}</p>
<p>Maximum # of items allowed in inventory: {{max_items}}</p>
<p>% Full: {{percent_full}}%</p>
</div>
</template>
Calculating the number of current items seems fairly straightforward, I pull all the products, sum the item counts (using d3), return the result.
Template.store.current_items = function () {
var store_id = this._id;
var items = Product.find({store_id: store_id}).fetch();
if ( items.length > 0 ) {
var item_cnt = d3.sum(_.pluck(items, 'item_count'));
return item_cnt;
}
else {
return 'N/A';
}
};
To calculate a percentage comparing the total # of allowed items, and the current items, it seems like I have to reduplicate everything. Is there a better way to do this?
Template.store.percent_full = function () {
var store_id = this._id;
var items = Product.find({store_id: store_id}).fetch();
if ( items.length > 0 ) {
var item_cnt = d3.sum(_.pluck(items, 'item_count'));
return item_cnt / max_items * 100;
}
else {
return 'N/A';
}
};
Extract the duplicated logic to a separate function and just call it appropriately from different helpers. This is not really different from DRYing any other JS code.
Related
İ want to find sufficient stock according to sale.
Measure =
VAR _date = VALUES(dateofdata)
VAR opening_date = VALUES('Stores' [Opening date])
VAR dayfornewstores = _date-opening_date
VAR _days = 28
VAR avg_sales = IF(opening_date<28,[TotalSales]/opening_date,[TotalSales]/_days)
VAR qual_days = [TotalStocks]/avg_sales
RETURN qual_days
When I use this measure in table for each store it is success. But when I want all of my stores number of days to qualify for sale in card visual there is error. How can I solve it?
I have a task to compare 2 dynamic periods from the table (MOO).
Th idea is to get clients which are in both dates and compare them by 1 field (Rating_rank).
But my created field calculates max from all table, not grouping by client.
What should id do?
rate_worse_tab =
var min_dt = calculate(min('MOO'[value_day]),ALLSELECTED('MOO'[value_day]))
var max_dt = calculate(max('MOO'[value_day]),ALLSELECTED('moo'[value_day]))
var cur_cl = CALCULATETABLE(values(MOO[CLIENT_UK]),filter(MOO,MOO[VALUE_DAY]=max_dt))
var old_cl = CALCULATETABLE(values(MOO[CLIENT_UK]),filter(MOO,MOO[VALUE_DAY]=min_dt))
var combo_table = CALCULATETABLE(values(MOO[CLIENT_UK]),filter(MOO, MOO[CLIENT_UK] in cur_cl && MOO[CLIENT_UK] in old_cl))
var f_table = ADDCOLUMNS(combo_table,"Old_rate_rank",calculate(max(MOO[Rating_rank]),filter(MOO,MOO[VALUE_DAY]=min_dt && MOO[CLIENT_UK] in combo_table)))
return
f_table
I have a little puzzle that annoys me in PowerBI/DAX. I'm not looking for workarounds - I am looking for an explanation of what is going on.
I've created some sample data to reconstruct the problem.
Here are my two sample tables written in DAX:
Events =
DATATABLE (
"Course", STRING,
"WeekNo", INTEGER,
"Name", STRING,
"Status", STRING,
{
{ "Python", 1, "Joe", "OnSite" },
{ "Python", 1, "Donald", "Video" },
{ "DAX", 2, "Joe", "OnSite" },
{ "DAX", 2, "Hillary", "Video" },
{ "DAX", 3, "Joe", "OnSite" },
{ "DAX", 3, "Hillary", "OnSite" },
{ "DAX", 3, "Donald", "OnSite" }
}
)
WeekNumbers =
DATATABLE ( "WeekNumber", INTEGER, { { 1 }, { 2 }, { 3 }, { 4 } } )
I have a table with events and another table with all weeknumbers and there is a (m:1) relation between them on the weekNo/weeknumber (I've given them different names to easily distinguish them in this example). I have a slicer in PowerBI on the weeknumber. And I have a table which shows aggregation and counts the participants based on the status with the following measures:
#OnSite = COUNTROWS(FILTER(events,Events[Status]="OnSite"))
#Video = COUNTROWS(FILTER(events,Events[Status]="Video"))
I visualize the two measures in a table together with the Course and the weekNo. With the slicer on weekNumber 3 there are nobody with status video so #video is blank. See screenshot.
Then I decided to create a new measure which should show a 0 instead of blank for the #video:
#VideoWithZero = VAR counter=COUNTROWS(FILTER(events,Events[Status]="Video"))
RETURN IF(ISBLANK(counter),0,counter)
I add the #VideoWithZero to the table and get a lot of extra rows in the table for the other weekNo's:
So my question is - Why do I get the extra rows for week 1 and 2 in the table? I would expect my filter on WeekNumber to filter them out.
The filter is being applied to the context of the query executed, and then the measures are calculated. Now the issue is that one of your measures is always returning a value (0), so regardless of your context it will always show a result, thus making it seem that it is ignoring the filter.
One way I tend to get implement this is by providing some additional context to when I might want to show 0 instead of blank. In your case it would be when one of the counts is not blank:
#OnSite =
VAR video = COUNTROWS(FILTER(events,Events[Status]="Video"))
VAR onsite = COUNTROWS(FILTER(events,Events[Status]="OnSite"))
RETURN IF(ISBLANK(video), onsite, onsite + 0) //+0 will force it not to be blank
#Video =
VAR video = COUNTROWS(FILTER(events,Events[Status]="Video"))
VAR onsite = COUNTROWS(FILTER(events,Events[Status]="OnSite"))
RETURN IF(ISBLANK(onsite), video, video + 0)
So on the OnSite measure it will check if there are Videos and if so, it adds +0 to the result of the OnSite count to force it not to be blank (and vice versa)
One other way could be to count total rows and subtract the ones different to the status you need:
#OnSite =
VAR total= COUNTROWS(Events[Status])
VAR notOnsite = COUNTROWS(FILTER(events,Events[Status]<>"OnSite"))
RETURN total - notOnsite
#Video =
VAR total= COUNTROWS(Events[Status])
VAR notVideo= COUNTROWS(FILTER(events,Events[Status]<>"Video"))
RETURN total - notVideo
Using the Northwind database from RavenDB tutorial I'm trying to group orders by employee and get the most resent order for every employee.
Map:
from order in docs.Orders
select new {
Employee = order.Employee,
Count = 1,
MostRecent = order.OrderedAt,
MostRecentOrderId = order.Id
}
Reduce with nonexisting MaxBy:
from result in results
group result by result.Employee into grp
select new {
Employee = grp.Key,
Count = grp.Sum(result => result.Count),
MostRecent = grp.Max(result => result.MostRecent),
MostRecentOrderId = grp.MaxBy(result => result.MostRecent).MostRecentOrderId,
}
Reduce attempt:
from result in results
group result by result.Employee into grp
let TempMostRecent = grp.Max(result => result.MostRecent)
select new {
Employee = grp.Key,
Count = grp.Sum(result => result.Count),
MostRecent = TempMostRecent,
MostRecentOrderId = grp.First(result => result.MostRecent == TempMostRecent).MostRecentOrderId
}
However my reduce attempt returns 0 results.
Also: will RavenDB treat the Order.OrderetAt as a proper DateTime value and order them correctly?
You need to do it like
from order in docs.Orders
select new {
Employee = order.Employee,
Count = 1,
MostRecent = order.OrderedAt,
MostRecentOrderId = order.Id
}
from result in results
group result by result.Employee into grp
let maxOrder = grp.OrderByDescending(x=>x.MostRecent).First()
select new {
Employee = grp.Key,
Count = grp.Sum(result => result.Count),
MostRecent = maxOrder.MostRecent,
MostRecentOrderId = maxOrder.MostRecentOrderId,
}
I have some documents in my Couchbase with the following template:
{
"id": 102750,
"status": 5,
"updatedAt": "2014-09-10T10:50:39.297Z",
"points1": 1,
"points2": -3,
"user1": {
"id": 26522,
...
},
"user2": {
"id": 38383,
...
},
....
}
What I want to do is to group the documents on the user and sum the points for each user and then show the top 100 users in the last week. I have been circling around but I haven't come with any solution.
I have started with the following map function:
function (doc, meta) {
if (doc.user1 && doc.user2) {
emit(doc.user1.id, doc.points1);
emit(doc.user2.id, doc.points2);
}
}
and then tried the sum to reduce the results but clearly I was wrong because I wasn't able to sort on the points and I couldn't also include the date parameter
you need to see my exemple I was able to group by date and show the values with reduce. but calculate the sum I did it in my program.
see the response How can I groupBy and change content of the value in couchbase?
I have solved this issue by the help of a server side script.
What I have done is I changed my map function to be like this:
function (doc, meta) {
if (doc.user1 && doc.user2) {
emit(dateToArray(doc.createdAt), { 'userId': doc.user1.id, 'points': doc.points1});
emit(dateToArray(doc.createdAt), { 'userId': doc.user2.id, 'points': doc.points2});
}
}
And in the script I query the view with the desired parameters and then I group and sort them then send the top 100 users.
I am using Node JS so my script is like this: (the results are what I read from couchbase view)
function filterResults(results) {
debug('filtering ' + results.length + ' entries..');
// get the values
var values = _.pluck(results, 'value');
var groupedUsers = {};
// grouping users and sum their points in the games
// groupedUsers will be like the follwoing:
// {
// '443322': 33,
// '667788': 55,
// ...
// }
for (var val in values) {
var userId = values[val].userId;
var points = values[val].points;
if (_.has(groupedUsers, userId)) {
groupedUsers[userId] += points;
}
else
groupedUsers[userId] = points;
}
// changing the groupedUsers to array form so it can be sorted by points:
// [['443322', 33], ['667788', 55], ...]
var topUsers = _.pairs(groupedUsers);
// sort descending
topUsers.sort(function(a, b) {
return b[1] - a[1];
});
debug('Number of users: ' + topUsers.length + '. Returning top 100 users');
return _.first(topUsers, 100);
}