In AgGrid, I am tyring to use aggregation functions and valuegetter on the same column. It seems that because of valuegetter, my aggreation functions are not working, I just get 0 or null value on aggregation. Could you please check my code for any possible solutions?
Thanks.
{ headerName: "Price", filter: "agNumberColumnFilter", valueGetter: priceValueGetter, allowedAggFuncs: ['avg', 'sum', 'min', 'max']};
function priceValueGetter(params) {
var value = '';
if (params.data) {
var EPrice = params.data.a;
var p1 = params.data.b;
if (EPrice && p1) {
value = (EPrice - p1).toFixed(2);
}
}
return value;
}
Your valueGetter is returning a string.
So the built-in aggregation functions, which expect numbers, are failing.
Try this valueGetter instead:
function priceValueGetter(params): number {
if (params.data && params.data.a && params.data.b) {
return params.data.a - params.data.b
}
return null;
}
This won't format the way that you want, but you can fix that by adding a valueFormatter.
One other piece of advice - given that you have a variable named 'EPrice', I'm assuming that you're dealing with money. You shouldn't depend on Javascript's native 'number' type for money, as it is essentially a 'float', and rounding errors will ensue. There are lots of articles about how to properly handle monetary values in Javascript - I personally use a Javascript port of Java's BigDecimal class, called 'big.js', but there are several other solutions.
Edit - also be careful of using if on a number - it evaluates to false if the number is zero. If that's what you want, fine, but if it isn't, adjust your logic accordingly.
Related
I'm a backend dev moved recently onto js side. I was going through a tutorial and came across the below piece of code.
clickCreate: function(component, event, helper) {
var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
return validSoFar && inputCmp.get('v.validity').valid;
}, true);
// If we pass error checking, do some real work
if(validExpense){
// Create the new expense
var newExpense = component.get("v.newExpense");
console.log("Create expense: " + JSON.stringify(newExpense));
helper.createExpense(component, newExpense);
}
}
Here I tried to understand a lot on what's happening, there is something called reduce and another thing named validSoFar. I'm unable to understand what's happening under the hood. :-(
I do get the regular loops stuff as done in Java.
Can someone please shower some light on what's happening here. I should be using this a lot in my regular work.
Thanks
The reduce function here is iterating through each input component of the expense form and incrementally mapping to a boolean. If you have say three inputs each with a true validity, the reduce function would return:
true && true where the first true is the initial value passed into reduce.
true && true and where the first true here is the result of the previous result.
true && true
At the end of the reduction, you're left with a single boolean representing the validity of the entire, where by that if just a single input component's validity is false, the entire reduction will amount to false. This is because validSoFar keeps track of the overall validity and is mutated by returning the compound of the whether the form is valid so far and the validity of the current input in iteration.
This is a reasonable equivalent:
var validExpense = true;
var inputCmps = component.find('expenseform')
for (var i = 0; i < inputCmps.length; i++) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
if (!inputCmp.get('v.validity').valid) {
validExpense = false;
}
}
// Now we can use validExpense
This is a somewhat strange use of reduce, to be honest, because it does more than simply reducing a list to a single value. It also produces side effects (presumably) in the call to showHelpMessageIfInvalid().
The idea of reduce is simple. Given a list of values that you want to fold down one at a time into a single value (of the same or any other type), you supply a function that takes the current folded value and the next list value and returns a new folded value, and you supply an initial folded value, and reduce combines them by calling the function with each successive list value and the current folded value.
So, for instance,
var items = [
{name: 'foo', price: 7, quantity: 3},
{name: 'bar', price: 5, quantity: 5},
{name: 'baz', price: 19, quantity: 1}
]
const totalPrice = items.reduce(
(total, item) => total + item.price * item.quantity, // folding function
0 // initial value
); //=> 65
It does not make sense to use reduce there and have side effects in the reduce. Better use Array.prototype.filter to get all invalid expense items.
Then use Array.prototype.forEach to produce side effect(s) for each invalid item. You can then check the length of invalid expense items array to see it your input was valid:
function(component, event, helper) {
var invalidExpenses = component.find('expenseform').filter(
function(ex){
//return not valid (!valid)
return !ex.get('v.validity').valid
}
);
invalidExpenses.forEach(
//use forEach if you need a side effect for each thing
function(ex){
ex.showHelpMessageIfInvalid();
}
);
// If we pass error checking, do some real work
if(invalidExpenses.length===0){//no invalid expense items
// Create the new expense
var newExpense = component.get("v.newExpense");
console.log("Create expense: " + JSON.stringify(newExpense));
helper.createExpense(component, newExpense);
}
}
The mdn documentation for Array.prototype.reduce has a good description and examples on how to use it.
It should take an array of things and return one other thing (can be different type of thing). But you won't find any examples there where side effects are initiated in the reducer function.
few weeks ago I've started to work with typescript and knockoutJS, I have a specific problem and yet I have solution for it, it's so ugly I can't stand that, but can't get anything better from it, there's too much code to be pasted, but i'll try to describe my problem the best I can:
I have two view models that communicate with the same data model. Let's say that model is an array of Simple Objects called Numbers. Every Number has following properties: Value, isMinValueEnabled, minValue, isMaxValueEnabled, maxValue, isStepEnabled, stepValue, valueFormat. valueFormat might be numeric or percentage (so that value, min, max and step are multiplied by 100). I can activate minimum, maximum and step values and deactivate them. Then save data to the model and do exactly the same (with some restrictions) in another viewModel.
The problem is with those optional parameters and percentage values, because when I'm reading data I firstly check if Number is percentage or not and if every property is Enabled. Then I eventually multiply value by 100 if it is set. I have to do the same operation when I'm saving data, that is check every number for format and is*Enabled and eventually divide by 100. With 3-4 properties there is no problem, but now I have to write few more optional properties that depend's on the format and enabled/disabled state and I'm getting into troubles with ton's of if's statements, I myself can't even read that. Is there some better patter that can be used in this situation?
EDIT
Ok, so things look like this: I have a series of numbers, they can look like:
100, 2 000, 34 000.21, 2.1k, 2.11M, 22% but those are only display values whereas real values should stand like this for the example given: 100, 2000, 34000.21, 2100, 2110000, 0.22. The user can edit the value to anything else, like, let's say has 22% in input and then edit this into 1k. I shall convert 1k to original value which is 1000 and check if minimumValue and maximumValue for that number are set. If they are, I will check, and let's say maxValue is 800, then user input can no longer be 1k, but 0.8k instead because he can not get out of maximumValue. MinimumValue, MaximumValue, StepValue and so on are properties of every single Number. I was playing with ko.pureComputed, but I need to abstract it somehow:
var f = ko.computed(<KnockoutComputedDefine<number>>{
read: ...
write: ...
});
What I have now is totally ugly and looks like this:
export class Variable {
[...]
public inputType: KnockoutObservable<VariableInputType>;
public typeAndFormat: KnockoutObservable<DataTypeFormat>;
public isMinEnabled: KnockoutObservable<boolean>;
public minValue: KnockoutObservable<number>;
public isMaxEnabled: KnockoutObservable<boolean>;
public maxValue: KnockoutObservable<number>;
public isStepEnabled: KnockoutObservable<boolean>;
public stepValue: KnockoutObservable<number>;
public value: KnockoutObservable<number>;
[...]
constructor(...) {
[...]
this.inputType = ko.observable(VariableInputType.Input);
this.typeAndFormat = ko.observable(variable.typeAndFormat || DataTypeFormat.Number);
if (variable.minValue !== null) {
this.isMinEnabled = ko.observable(true);
this.minValue = ko.observable(variable.minValue);
} else {
this.isMinEnabled = ko.observable(false);
this.minValue = ko.observable(null);
}
if (variable.maxValue !== null) {
this.isMaxEnabled = ko.observable(true);
this.maxValue = ko.observable(variable.maxValue);
} else {
this.isMaxEnabled = ko.observable(false);
this.maxValue = ko.observable(null);
}
if (variable.step !== null) {
this.isStepEnabled = ko.observable(true);
this.stepValue = ko.observable(variable.step);
} else {
this.isStepEnabled = ko.observable(false);
this.stepValue = ko.observable(null);
}
if (variable.defaultValue !== null) {
this.value = ko.observable(variable.defaultValue);
} else {
this.value = ko.observable(0);
}
if (this.typeAndFormat() === DataTypeFormat.NumberPercentage) {
this.value(this.value() * 100);
if (this.isMinEnabled()) this.minValue(this.minValue() * 100);
if (this.isMaxEnabled()) this.maxValue(this.maxValue() * 100);
if (this.isStepEnabled()) this.stepValue(this.stepValue() * 100);
}
[...]
this.isMinEnabled.subscribe((v) => { if (v !== true) this.minValue(null) }, this);
this.isMaxEnabled.subscribe((v) => { if (v !== true) this.maxValue(null) }, this);
this.isStepEnabled.subscribe((v) => { if (v !== true) this.stepValue(null)}, this);
[...]
}
public getModifiedVariable() {
[...]
this.originalData.typeAndFormat = this.typeAndFormat();
this.originalData.minValue = this.minValue();
this.originalData.maxValue = this.maxValue();
this.originalData.step = this.stepValue();
this.originalData.defaultValue = this.value();
[...]
if (this.typeAndFormat() === DataTypeFormat.NumberPercentage) {
this.originalData.defaultValue = this.originalData.defaultValue / 100;
if (this.isMinEnabled()) this.originalData.minValue = this.originalData.minValue / 100;
if (this.isMaxEnabled()) this.originalData.maxValue = this.originalData.maxValue / 100;
if (this.isStepEnabled()) this.originalData.step = this.originalData.step / 100;
}
[...]
return this.originalData;
};
[...]
}
The second viewmodel that has even more validation and restrictions looks even worse... I don't really know how I could abstract that so that it would be readable for me and for others.
There are two different problems
you need a custom visualization, that needs formatting, and a custom data input, that needs parsing
you need to add some business logic (validation of values)
The first question can be solved by using an extender. With this technique your observable must store the actual value, not the formatted value. You can use it to add a child observable, which could be called formattedValue. This must be a writable computed observable, which two functions:
read: format the underlying actual value, and return it formatted, so that the user has a beautiful view of the value
write: parse the value received from the user input, and store the result in the underlying actual value
You can find examples of extenders like theses ones: Three Useful Knockout Extenders. The extenders can recevie parameters, so that they can be configured individually (in your case you can set percentage, steps, and so on). Another big example of this technique is the ko.valdiation library.
If you use this technique, in the HTML you need to bind the child observable, instead of the underlying observable with the real value, i.e.:
<input type="text" data-bind="value: vm.someValue.formattedValue"/>
As explained, the formattedValue is a new child observable which formats/parses the value.
The second question can also be solved with writable computed observables. You can add the validation logic in the write method, so that any time the value is modified, it's validated, and rejected or corrected, depending on what you want to do. The computed observable can access other values from the view model, so its implementation should be easy. Of course, the validation logic must access the observables with the actual values. I.e it can completely ignore if the observable is extended or not.
The great advantage of this implementation is that you can implement an test each required functionality independently:
implement and test the parsing/formatting extenders, the format/parse in the extenders
implementa and test the the business logic in the writable computed observables
Once implemented an tested, start using them together.
I'm trying to use map-reduce to understand when this can be helpful.
So I have a collection named "actions" with 100k docs like this:
{
"profile_id":1111,
"action_id":2222
}
Now I'm trying to do map-reduce examples. I'm trying to get a list of "all users and total actions each one has". Is this possible? My code:
db.fbooklikes.mapReduce(
function(){
emit(this.profile_id, this.action_id);
},
function(keyProfile, valueAction){
return Array.sum(valueAction);
},
{
out:"example"
}
)
.. This is not working. The result is:
"counts" : {
"input" : 100000,
"emit" : 100000,
"reduce" : 1146,
"output" : 13
},
"ok" : 1,
"_o" : {
"result" : "map_reduce_example",
"timeMillis" : 2539,
"counts" : {
"input" : 100000,
"emit" : 100000,
"reduce" : 1146,
"output" : 13
},
"ok" : 1
},
What I'm trying to do is something possible with map-reduce?
Well yes you can use it, but the more refined response is that there are likely better tools for doing what you want.
MapReduce is handy for some tasks, but usually best suited when something else does not apply. The inclusion of mapReduce in MongoDB pre-dates the introduction of the aggregation framework, which is generally what you should be using when you can:
db.fbooklikes.aggregate([
{ "$group": {
"_id": "$profile_id",
"count": { "$sum": 1 }
}}
])
Which will simply return the counts for the all documents in the collection grouped by each value of "profile_id".
MapReduce requires JavaScript evaluation and therefore runs much slower than the native code functions implemented by the aggregation framework. Sometimes you have to use it, but in simple cases it is best not to, and there are some quirks that you need to understand:
db.fbooklikes.mapReduce(
function(){
emit(this.profile_id, 1);
},
function(key,values){
return Array.sum(values);
},
{
out: { "inline": 1 }
}
)
The biggest thing people miss with mapReduce is the fact that the reducer is almost never called just once per emitted key. In fact it will process output in "chunks", thus "reducing" down part of that output and placing it back to be "reduced" again against other output until there is only a single value for that key.
For this reason it is important to emit the same type of data from the reduce function as is sent from the "map" function. It's a sticky point that can lead to weird results when you don't understand that part of the function. It is in fact the underlying way that mapReduce can deal with large values of results for a single key value and reduce them.
But generally speaking, you should be using the aggregation framework where possible, and where a problem requires some special calculations that would not be possible there, or otherwise has some complex document traversal where you need to inspect with JavaScript, then that is where you use mapReduce.
You don't want to sum the action ids, you want to count them. So you want something like the following
var map = function () {
emit(this.profile_id, { action_ids : [this.action_id], count : 1 });
}
var reduce = function(profile_id, values) {
var value = { action_ids: [], count: 0 };
for (var i = 0; i < values.length; i++) {
value.count += values[i].count;
value.action_ids.push.apply(value.action_ids, values[i].action_ids);
}
return value;
}
db.fbooklikes.mapReduce(map, reduce, { out:"example" });
This will give you an array of action ids and a count for each profile id. The count could be obtained by accessing the length of the action_ids array, but I thought I would keep it separate to make the example clearer.
I have an observable item and I am trying to apply user driven formatting on that observable. I have an integer input box where a user can select how many decimal places, 0-6. I am trying to update the observable to apply decimal places based on the selection in that input box.
I first tried a computed value, which did not work. Returned an error that 'toFixed' was not a function.
this.formattedResult = ko.computed(function () {
var newValue = self.decimalValue();
var precision = self.decimalPlaces();
return newValue.toFixed(precision);
});
I then tried adding a binding handler, which results in 'uncaught object' on the 'toFixed'
ko.bindingHandlers.numericText = {
update: function (element, valueAccessor, allBindingsAccessor) {
var value = new String(ko.utils.unwrapObservable(valueAccessor()).toString());
var precision = ko.utils.unwrapObservable(allBindingsAccessor().precision);
var formattedValue = value.toFixed(precision);
ko.bindingHandlers.text.update(element, function () { return formattedValue; });
},
defaultPrecision: 1
};
Then I tried extending numeric with a numericText, which also results in a 'uncaught object' error on the 'toFixed'.
ko.extenders.numeric = function (target, precision) {
if (precision() != null) {
var precisionValue = target();
console.log(precisionValue);
var precisionDecimal = precisionValue.toFixed(2);
console.log(precisionDecimal);
var result = ko.dependentObservable({
read: function () {
return target().toFixed(precision());
},
write: target
});
result.raw = target;
return result;
}
else
return target;
};
The binding on the HTML elements were all updated to take these changes into account but they did not work. I also tried just simply adding 'toFixed' to the data binding in the HTML and that also did not work.
I found these similar questions but the solutions are not working for me.
Format knockout observable with commas and/or decimal places within html data binding, NOT in viewmodel
Formatting rules for numbers in KnockoutJS
Adding decimal formatting to Knockout number data bindings
This is a very frustrating problem as I thought it would be really simple to add decimal places in a computed function. What am I doing wrong?
I'm not sure if it matters but I am using knockout 3.1
Edit:
I also tried just simply adding 'toFixed' to the data-binding but that also results in an uncaught object.
<p data-bind="text: decimalValue().toFixed(2)"></p>
I didn't end up solving my problem in the way that I had anticipated but I modified the computed column to force the decimal to a float and then added the decimals to that float value.
The final function looks like this:
this.formattedResult = ko.computed(function () {
var newValue = parseFloat(self.decimalValue());
var precision = self.decimalPlaces();
return newValue.toFixed(precision);
});
I don't know why this works but the other does not? Maybe when I assign the value to newValue I'm just assigning an observable to it and the observable doesn't know how to handle toFixed()? Doesn't explain all of the other problems but at least this is fixed for me.
I recommend using the following JavaScript Number constructor which will return NaN if the number is invalid.
this.formattedResult = ko.computed(function () {
// call the number constructor. newValue is now a number or NaN
//we choose a default value as well.
var newValue = isNaN(self.decimalValue()) ? 0 :
Number(self.decimalValue());
// call the number constructor again
var precision = isNaN(self.decimalPlaces()) ? 0 : Number(self.decimalPlaces());
return newValue.toFixed(precision);
});
I want to have a Backbone model with float attributes in it but without worrying too much about variable types.
I would like to encapsulate the value parsing right there in the model so I am thinking of overriding the set function:
var Place = Backbone.Model.extend({
set: function(attributes, options) {
if (!_.isEmpty(attributes.latitude)){
attributes.latitude == parseFloat(attributes.latitude);
}
if (!_.isEmpty(attributes.longitude)){
attributes.longitude == parseFloat(attributes.longitude);
}
Backbone.Model.prototype.set.call(this, attributes, options);
}
});
However this seems cumbersome, since I would have a similar logic in the validate method and potentially repeated across multiple models. I don't think the View should take care of these conversions.
So what is the best way of doing it?
Use a validation plugin for your model so that you can validate the input in a generic fashion.
There are several out there including one that I have written:
Backbone.Validator
Backbone.Validation
Then you don't worry about performing data validation anywhere else - your model does it and sends out and error message you can listen for and provide appropriate feedback.
Also, a lat/lng pair can, in rare circumstances, be an integer, such as Greenwich England: 0,0 or the north pole: 90,180. And since JavaScript only has "number" any valid input for parseFloat is also valid for parseInt.
But parseFloat will always return a float.
My solution was to replace Backbone.Model.prototype.set with a preprocessor proxy:
/**
* Intercept calls to Backbone.Model.set and preprocess attribute values.
*
* If the model has a <code>preprocess</code> property, that property will be
* used for mapping attribute names to preprocessor functions. This is useful
* for automatically converting strings to numbers, for instance.
*
* #param Backbone
* the global Backbone object.
*/
(function(Backbone) {
var originalSet = Backbone.Model.prototype.set;
_.extend(Backbone.Model.prototype, {
set: function(key, val, options) {
if(!this.preprocess) {
return originalSet.apply(this, arguments);
}
// If-else copied from Backbone source
if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
for(attr in this.preprocess) {
if(_.has(attrs, attr)) {
attrs[attr] = this.preprocess[attr](attrs[attr]);
}
}
return originalSet.call(this, attrs, options);
},
});
})(Backbone);
After this, models with a preprocess property will use it to map attribute names to preprocessor functions. For instance, preprocess: { age: parseInt } means that whenever the age attribute is set, the value will be passed through parseInt before actually setting it. Attributes with no corresponding preprocess entry will not be affected.
Example usage:
var Thing = Backbone.Model.extend({
preprocess: {
mass: parseInt,
created: function(s) { return new Date(s); },
},
});
var t = new Thing({
label: '42',
mass: '42',
created: '1971-02-03T12:13:14+02:00',
});
console.log(t.get('label')+3); // 423
console.log(t.get('mass')+3); // 45
console.log(t.get('created').toLocaleString('ja-JP', { weekday: 'short' })); // 水
Pros
The functionality is available in all models without needing to duplicate code
No need to send { validate: true } in every call to set
No need to duplicate preprocessing in validate, since this happens before validate is called (this might also be a con, se below)
Cons
Some duplication of Backbone code
Might break validation since preprocessing happens before validate is called. JavaScript parsing methods usually return invalid values instead of throwing exceptions, though (i.e. parseInt('foo') returns NaN), so you should be able to detect that instead.