我正在尝试将groupBy与RxJs一起使用,我需要使用对象作为键.如果我没有,我使用,例如,像这样的简单字符串:
var types = stream.groupBy(
function (e) { return e.x; }); //x is a string
然后一切都很好,我的订阅被调用一次,只有一次,每个不同的键.但是如果我尝试使用对象,则会从流中调用每个元素的订阅,即使密钥恰好与之前的密钥相同.
当然存在关于对象相等的问题,但这是我感到困惑的地方,因为我不明白如何使用groupBy的附加参数.最新版本的文档说有第三个参数可以作为比较器,但它永远不会被调用.早期的文档谈到了一个关键的序列化器,这是一个完全不同的想法,但这两种方式都不适合我.
看看Rx源代码,我看到了检查getHashCode函数的尝试,但我没有找到并记录它.像这样的代码:
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?
是我想写的,但没有运气,我为第三次回调所做的任何事都没有被调用.
这有什么不对?
解决方法:
最简单的解决方案是确保您的key函数返回一个数字或字符串.但是如果你真的想要返回一个对象作为你的密钥,那么你可以使用comparer和hashCode来帮助groupBy.您可以使用valueOf代替hashCode(它要求您返回一个数字),它允许您返回一个字符串.
hashCode和valueOf应该类似于c# Object.GetHashCode.
他们应该返回一个值,使得:
>两个被认为相等的键每次都应返回相同的值.
>两个被认为不相等的键通常应返回不同的值(以最小化冲突).您可以越好地确保这一点,字典的效率就越高.
区别在于hashCode应该返回一个数字而valueOf可以返回一个数字或一个字符串.因此valueOf更容易实现.
比较器的规则是它需要2个键值,并且应该返回true表示相等,而false表示不等式.
所以,你可以把你的例子写成:
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);
但是如果你的valueOf函数实际上产生了与你的比较器匹配的唯一值,并且你并不真正关心键是否作为一个实际对象在下游传输,那么只需简化你的生活并将你的键转换为字符串并使用字符串关键是这样的:
var types = stream.groupBy(
function (e) { return JSON.stringify({ x: e.x, y: e.y }); },
function (e) { return e; });