I'm trying to use groupBy with RxJs, and I need to use objects as keys. If I don't and I use, for example, simple strings like this:
var types = stream.groupBy(
function (e) { return e.x; }); //x is a string
then everything goes fine and my subscription is called once, and once only, for each different key. But if I try with objects, the subscription is called for each element from stream, even if a key happens to be the same as previous ones.
There is of course a problem about object equality, but this is where I get confused because I don't understant how to use the additional arguments for groupBy. The latest version of docs says there have a 3rd argument that can be a comparer, but it never gets called. Earlier docs talk about a key serializer which is a totally different idea, but neither ways work for me.
Looking at Rx source code, I see attempts to check for a getHashCode function, but I do not find and documentation about it. Code like this:
var types = stream.groupBy(
function (e) {
return { x: e.x, y: e.y }; //is this valid at all?
},
function (e) { return e; },
function (...) { return ???; }); //what am I supposed to do here?
is what i'm trying to write, but no luck, and whatever I put for the 3rd callback is not called.
What's wrong here?
The simplest solution is to make sure your key function returns a number or string. But if you really want to return an object as your key, then you can use comparer and hashCode to help groupBy. Instead of hashCode (which requires you return a number), you can use valueOf which lets you return a string.
hashCode and valueOf should work similar to the c# Object.GetHashCode.
They should return a value such that:
Two keys that are considered equal should return the same value every time.
Two keys that are considered not equal should usually return a different value (to minimize collisions). The better you can ensure this, the more efficient the dictionary will be.
The difference is hashCode should return a number and valueOf can return a number or a string. Thus valueOf is easier to implement.
The rules for the comparer is that it takes 2 key values and should return true to indicate equality and false to indicate inequality.
So, you might write your example as:
var valueOf = function () {
return JSON.stringify(this);
};
var keyCompare = function (a, b) { return a.x === b.x && a.y === b.y; };
var types = stream.groupBy(
function (e) {
return { x: e.x, y: e.y, valueOf: valueOf }; //is this valid at all?
},
function (e) { return e; },
keyCompare);
But if your valueOf function is actually producing unique values that match your comparer and you don't really care about whether the key is tramsitted downstream as an actual object, then just make your life easier and transform your key into a string and use a string key like so:
var types = stream.groupBy(
function (e) { return JSON.stringify({ x: e.x, y: e.y }); },
function (e) { return e; });
Related
Languages such as Python and Java have special methods for sorting custom classes. In JavaScript, toString() can be overridden, but this does not work easily for numeric values.
As a workaround, I added a method called compareTo() to the class, although this still requires a function to call it.
class NumericSortable {
constructor(newVal) {
this.val = newVal
}
compareTo(other) {
return this.val - other.val
}
}
const objectList = [
new NumericSortable(3),
new NumericSortable(1),
new NumericSortable(20),
]
objectList.sort(
function(a, b) { return a.compareTo(b) })
console.log(objectList)
Is there a way to modify the class so it can be sorted without requiring a function to be defined inside sort()?
Perhaps there is a good way to override toString() that will work for numeric values. However, solutions such as localeCompare() or a collator require two arguments, and they would not be passed to the overridden toString() method at the same time.
You can add a static method to your NumericSortable class, and pass that into the sort call. This idea can be extended to any custom class that need to define how two instances are to be compared for sorting.
class NumericSortable {
constructor(value) {
this.value = value;
}
static compare(a,b) {
return a.value - b.value;
}
}
const arr = [
new NumericSortable(3),
new NumericSortable(1),
new NumericSortable(20),
];
arr.sort(NumericSortable.compare);
console.log(arr);
This makes things more explicit, and easier for anyone else reading the code to reason about how the array is being sorted.
I like to make a function that returns a sort function for these cases.
function by(prop){
return function(a,b){return a[prop]-b[prop];};
}
this let's you specify the object's to-be-sorted property at call-time, letting one generic function do a lot of heavy lifting.
objectList.sort(by("val"))
This avoids the need for a custom callback each sort, though with fat arrows that's not the burden it used to be anyway...
If no comparator is provided, each time two items in the array are compared, they'll be coerced to strings, and then those strings will be compared lexiographically to determine which object will come before the other in the sorted array. So, if you don't want to pass a comparator, adding a toString method to implement the desired sorting logic is the only other approach.
Unfortunately, for your situation, lexiographic comparison alone based on the .vals won't cut it; 1 will come before 20, and 20 will come before 3. If the numbers involved won't get so high as to have es in their string version, you could .padStart the returned string so that each compared numeric string will have the same number of characters (thereby allowing lexiographic comparison to work).
class NumericSortable {
constructor(newVal) {
this.val = newVal
}
// Unused now
compareTo(other) {
return this.val - other.val
}
toString() {
return String(this.val).padStart(15, '0');
}
}
const objectList = [
new NumericSortable(3),
new NumericSortable(1),
new NumericSortable(20),
]
objectList.sort()
console.log(objectList)
You may wish to account for negative numbers too.
Still, this whole approach is a bit smelly. When possible, I'd prefer a comparator instead of having to fiddle with the strings to get them to compare properly.
From my above comment ...
"The OP needs to wrap the build-in sort method into an own/custom implementation of Array.prototype.sort. But why should one do it? Just for the sake of not writing a comparing sort-callback?"
Having said the above, I herby nevertheless provide an implementation of the above mentioned approach just in order to prove to the OP that it's manageable (after all it is exactly what the OP did ask for), but also to show to the audience that the effort (even though it's a one time effort) of doing so is much greater than other already provided suggestions / solutions.
class NumericSortable {
constructor(newVal) {
this.val = newVal;
}
compareTo(other) {
return this.val - other.val;
}
}
const objectList = [
new NumericSortable(3),
new NumericSortable(1),
new NumericSortable(20),
];
objectList
// - sorting by overwritten custom `sort` function with
// an already build-in `compareTo` based custom compare
// function and no additionally passed compare function.
.sort();
console.log({ objectList });
objectList
// - reverse sorting by overwritten custom `sort` function
// with an additionally passed compare function.
.sort((a, b) => (-1 * a.compareTo(b)));
console.log({ objectList });
console.log(
'... simple sort-implementation defaul-test ...\n[1,4,9,0,6,3].sort() ...',
[1,4,9,0,6,3].sort()
);
console.log(
'... simple sort-implementation defaul-test ...\n["foo", "biz", "baz", "bar"].sort() ...',
["foo", "biz", "baz", "bar"].sort()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (arrProto) {
// save the native `sort` reference.
const coreApiSort = arrProto.sort;
// type detection helper.
function isFunction(value) {
return (
'function' === typeof value &&
'function' === typeof value.call &&
'function' === typeof value.apply
);
}
// different comparison helper functionality.
function defaultCompare(a, b) {
return (a < b && -1) || (a > b && 1) || 0;
}
function localeCompare(a, b) {
return a?.localeCompare?.(b) ?? defaultCompare(a, b);
}
function customDefaultCompare(a, b) {
const isCustomComparableA = isFunction(a.compareTo);
const isCustomComparableB = isFunction(b.compareTo);
return (isCustomComparableA && isCustomComparableB)
? a.compareTo(b)
: localeCompare(a, b);
}
// the new `sort` functionality.
function customSort(customCompare) {
return coreApiSort
// - (kind of "super") delegation to the
// before saved native `sort` reference ...
.call(this, (
// ... by using a cascade of different
// comparison functionality.
isFunction(customCompare)
&& customCompare
|| customDefaultCompare
));
}
Object
// - overwrite the Array prototype's native `sort`
// method with the newly implemented custom `sort`.
.defineProperty(arrProto, 'sort', {
value: customSort,
});
}(Array.prototype));
</script>
I am wondering why I need the PhoneNumberFormatter.prototype.slice method below.
Why can't I just use slice(3,6).join('') inside my other methods without needing to add PhoneNumberFormatter.prototype.slice method? When the interpreter doesn't find the method on the PhoneNumberFormatter object, wouldn't it just look up the prototype chain to find slice and join on the Array prototype?
function PhoneNumberFormatter(numbers) {
this.numbers = numbers;
}
PhoneNumberFormatter.prototype.render = function() {
var string = '';
string += this.parenthesize(this.getAreaCode());
string += ' ';
string += this.getExchangeCode();
string += '-';
string += this.getLineNumber();
return string;
};
PhoneNumberFormatter.prototype.getAreaCode = function() {
return this.slice(0, 3);
};
PhoneNumberFormatter.prototype.getExchangeCode = function() {
return this.slice(3, 6);
};
PhoneNumberFormatter.prototype.getLineNumber = function() {
return this.slice(6)
};
PhoneNumberFormatter.prototype.parenthesize = function(string) {
return '(' + string + ')';
};
// why do I need the following method?
PhoneNumberFormatter.prototype.slice = function(start, end) {
return this.numbers.slice(start, end).join('');
};
var phoneNumberOne = new PhoneNumberFormatter([6, 5, 0, 8, 3, 5, 9, 1, 7, 2]);
phoneNumberOne.render()
I guess it was created to make the code cleaner and prevents code duplication.
As the use of the slice keyword in two different places seems to confuse you I'll explain briefly the differences.
In your prototype methods (e.g. getAreaCode, getExchangeCode, ...), the keyword this represents a PhoneNumberFormatter object. When you call this.slice() (in the methods), you are calling the slice method of this object hence the one created in your code.
But in your slice method (the one in your code), you are calling this.numbers.slice(). Here you call the slice method on an array (this.numbers). You are using the native array method slice.
You could write your methods like so and remove the slice method created in your code:
PhoneNumberFormatter.prototype.getAreaCode = function() {
return this.numbers.slice(0, 3).join('');
};
I think the main confusion you seem to have is the name of the method slice. This leads you to believe that it is the array method, but in fact it is not.
The slice method mentioned in your code is the slice method of the PhoneNumberFormatter, not the one of the array.
Because of this, the interpreter could never find the slice method in the prototype chain, because the prototype of the PhoneNumberFormatter would be object, and just calling slice would rather throw an error that the method is undefined.
The this.slice method refers to PhoneNumberFormatter.prototype.slice and this one will refer to it's own numbers property and will then call the slice method on the numbers Array.
Maybe it is simply easier to rename the code like:
function PhoneNumberFormatter( numberArr ) {
this.numbers = numberArr.slice(); // copy numbers
}
PhoneNumberFormatter.prototype.getAreaCode = function() {
return this.take(0, 3);
};
PhoneNumberFormatter.prototype.take = function() {
return Array.prototype.splice.apply( this.numbers, arguments ).join('');
};
var formatter = new PhoneNumberFormatter([0,1,2,3,4,5,6,7,8,9]);
console.log(formatter.getAreaCode());
Which maybe makes it more clear to you, that the take method is simply a utility method simplifying your code, cause be honest, why would you want to repeat for 5 different methods the slice and join part, and then take a potential risk of copying an error 5 times, or in case you need to change something, need to make the same change 5 times
I have a class, it has two methods: get(), set().
function A() {
var value = '';
this.get = function () {
return value;
};
this.set = function (v) {
value = v;
};
}
And I have a function f():
/**
*
* #param { String | Number } b
*/
function f(b){
var value;
if(b instanceof A){
value = b.get();
}else{
value = b;
}
}
I could create an object:
var test = new A();
test.set('Hello');
f(test); //Hello
f(10); //10
The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.
I heard it is a bad practice use this operator.
The question is: Is it imposible to get rid of instanceof operator from my code?
Maybe I should use force type conversion or use another getter, setter?
<--UPDATE-->>
I found simple solution, I could coerce b.get to boolean type and check it. It works, but maybe it has incidental effect.
value = (!!b.get) ? b.get(): b;
<--UPDATE 2-->>
Another way: value = (b.constructor = A) ? b.get(): b;
It's not the instanceof operator itself which is the problem, it's what you're using it for. Your function is expecting values of two different types: either an instance of A which needs to be handled a certain way or anything else which can be used as is.
The problem with this is a) why is your function allowing two different types to begin with and couldn't you harmonise that into just one type, and b) if it's accepting "anything" or "A", why A specifically and not something more general?
Assuming that you cannot reasonably change point a), you can at least make b) better:
if (typeof b.get == 'function') {
value = b.get();
}
You've just made your function a little more flexible and adaptable to future change by testing for the actual thing you're interested in: a get method. That b is an instanceof A is sort of irrelevant for the task at hand and may limit you in the future.
Can a function return two values?
Can a function return two values? { I came across this in C} Any possibility of something in JavaScript
You can't return two distinct values but you can return an object or array. This is perfectly valid, as long as you document the behavior for anyone else who might encounter the code. It's helpful if the two returned values are truly related. If not, it can become difficult to understand the function. And just returning a diverse array of values from an object would be considered a bad practice by many people since a function's purpose is usually to perform a discrete action which may return a discrete value.
// As an object
function() {
return {val1: "value 1", val2: "value 2"};
}
// As an array
function() {
return ["value 1", "value 2"];
}
Not directly, but no one can stop you from returning an Array with multiple elements:
function modify(a,b) {
return [a*2, b*2];
}
var res = multiple(5, 10);
console.log( res );
To push this a little further, we could use such a "mapped" result to call another method. Javascripts .apply() comes in pretty handy for this.
function add(v1, v2) {
return v1 + v2;
}
and we could call
add.apply( null, modify(2,4) );
this effectively would be like
add(4, 8) // === 12
this.String = {
Get : function (val) {
return function() {
return val;
}
}
};
What is the ':' doing?
this.String = {} specifies an object. Get is a property of that object. In javascript, object properties and their values are separated by a colon ':'.
So, per the example, you would call the function like this
this.String.Get('some string');
More examples:
var foo = {
bar : 'foobar',
other : {
a : 'wowza'
}
}
alert(foo.bar); //alerts 'foobar'
alert(foo.other.a) //alerts 'wowza'
Others have already explained what this code does. It creates an object (called this.String) that contains a single function (called Get). I'd like to explain when you could use this function.
This function can be useful in cases where you need a higher order function (that is a function that expects another function as its argument).
Say you have a function that does something to each element of an Array, lets call it map. You could use this function like so:
function inc (x)
{
return x + 1;
}
var arr = [1, 2, 3];
var newArr = arr.map(inc);
What the map function will do, is create a new array containing the values [2, 3, 4]. It will do this by calling the function inc with each element of the array.
Now, if you use this method a lot, you might continuously be calling map with all sorts of arguments:
arr.map(inc); // to increase each element
arr.map(even); // to create a list of booleans (even or odd)
arr.map(toString); // to create a list of strings
If for some reason you'd want to replace the entire array with the same string (but keeping the array of the same size), you could call it like so:
arr.map(this.String.Get("my String"));
This will create a new array of the same size as arr, but just containing the string "my String" over and over again.
Note that in some languages, this function is predefined and called const or constant (since it will always return the same value, each time you call it, no matter what its arguments are).
Now, if you think that this example isn't very useful, I would agree with you. But there are cases, when programming with higher order functions, when this technique is used.
For example, it can be useful if you have a tree you want to 'clear' of its values but keep the structure of the tree. You could do tree.map(this.String.Get("default value")) and get a whole new tree is created that has the exact same shape as the original, but none of its values.
It assigns an object that has a property "Get" to this.String. "Get" is assigned an anonymous function, which will return a function that just returns the argument that was given to the first returning function. Sounds strange, but here is how it can be used:
var ten = this.String["Get"](10)();
ten will then contain a 10. Instead, you could have written the equivalent
var ten = this.String.Get(10)();
// saving the returned function can have more use:
var generatingFunction = this.String.Get("something");
alert(generatingFunction()); // displays "something"
That is, : just assigns some value to a property.
This answer may be a bit superflous since Tom's is a good answer but just to boil it down and be complete:-
this.String = {};
Adds an object to the current object with the property name of String.
var fn = function(val) {
return function() { return(val); }
}
Returns a function from a closure which in turn returns the parameter used in creating the closure. Hence:-
var fnInner = fn("Hello World!");
alert(fnInner()); // Displays Hello World!
In combination then:-
this.String = { Get: function(val) {
return function() { return(val); }
}
Adds an object to the current object with the property name of String that has a method called Get that returns a function from a closure which in turn returns the parameter used in creating the closure.
var fnInner = this.String.Get("Yasso!");
alert(fnInner()); //displays Yasso!