我试图GroupBy
用这些参数实现一个方法
function GroupBy(keySelector, elementSelector, comparer) { // keySelector = function(e) { return e.ID } // elementSelector = function(e) { return e.Name } // comparer = { Equals: function(a,b) { return a==b }, GetHashCode:... } }
但是我不知道实现它的有效方法.
我用linq.js和我创建的方法创建了一个jsPerf测试,该方法不使用比较器,只适用于平面类型.(这里输出测试)
其他库(如下划线和Lo-Dash)不带comparer
参数.所以他们的实现是无关紧要的.
我的密钥可能是一个类,所以我需要一些东西来确定TKey
在不同的实例中是否相同.
所以基本上我要做的是复制C#Linq GroupBy
行为,这里记录.
样本输入:
var arrComplex = [ { N: { Value: 10 }, Name: "Foo" }, { N: { Value: 10 }, Name: "Bar" }, { N: { Value: 20 }, Name: "Foo" }, { N: { Value: 20 }, Name: "Bar" } ];
样本输出(或类似的东西):
[ { "Key": {"Value":10}, "Elements":["Foo","Bar"] }, { "Key": {"Value":20}, "Elements":["Foo","Bar"] } ]
有关如何实施它的任何想法?
为了赏金,我想你考虑一下:
关键可能是一个对象
如果某些属性相等,则两个对象可以相等
它应该与现有解决方案一样快或更快
结果可以是数组或对象,只要我可以获得按键分组的元素就没关系
好吧,我期待一个完整的答案.
我使用你的jsperf作为参考,用于一些更好的脚本点.我真的非常喜欢你的'哈希'代码,所以我完全偷了它.根据'browserscope'图表,Mine使用不同的方法生成用于制作哈希的字符串,这似乎更快一点,从而提高了性能.我在我的测试中包含了一个"过多的递归"概念证明,以证明它具有递归保护,如JSON.stringify和.toSource().
我的jsfiddle显示代码返回您需要的格式.我的jsperf似乎表明它优于发布的解决方案.我还包括linq.js解决方案,但它对我来说在FireFox中表现非常糟糕.它可以在Safari,Chrome和IE中运行,但不会比我快,除了在IE中.我甚至在手机上试过它,但我仍然有相同的性能差异.我已经在所有浏览器的最新版本中亲自测试了它与发布的解决方案并排,并且每个浏览器的执行时间约为40%.每个人的想法是什么?
这是我的代码:
var arr = [ { N: 10, Name: "Foo" }, { N: 10, Name: "Bar" }, { N: 20, Name: "Foo" }, { N: 20, Name: "Bar" } ]; var poc = { name:'blah', obj:{} }; poc.obj = poc; var arrComplex = [ { N: { Value: 10, TooMuchRecursionProofPOC:poc }, Name: "Foo" }, { N: { Value: 10, TooMuchRecursionProofPOC:poc }, Name: "Bar" }, { N: { Value: 20, TooMuchRecursionProofPOC:poc }, Name: "Foo" }, { N: { Value: 20, TooMuchRecursionProofPOC:poc }, Name: "Bar" } ]; var eArr = Enumerable.From(arr); var eArrComplex = Enumerable.From(arrComplex); function setup_hashers() { // recursion protection idea var rp = '_rp'+(Math.random()*10000000); function tstr() { var out = '', i = ''; if (this[rp]) { this[rp] = undefined; return out; } for (i in this) if (i != rp && this.hasOwnProperty(i)) out += this[i] instanceof Object ? ((this[rp] = true) && this[i] != this && !this[i][rp] ? tstr.call(this[i]) : '') : (this[i].toString || tstr).call(this[i]); return out; }; Number.prototype.GetHashCode = function() { return this.valueOf(); }; Object.prototype.GetHashCode = function() { var s = (this instanceof Object ? tstr : this.toString || tstr).call(this), h = 0; if (s.length) for (var i = 0; i < s.length; i++) h = ((h << 5) - h) + s.charCodeAt(i); return h; }; } function group_by(a, keyFunc, valFunc, comp, as_array) { if (!a.length) return as_array ? [] : {}; var keyFunc = keyFunc || function (e) { return e; }, valFunc = valFunc || function (e) { return e; }; var comp = comp || { Equals: function (a, b) { return a == b; }, Hash: function (e) { return e.GetHashCode(); } }; var hashs = {}, key = '', hash = ''; for (var i = 0; i < a.length; i++) { key = keyFunc(a[i]); hash = comp.Hash(key); if (typeof hashs[hash] != 'undefined') hash = comp.Equals(key, hashs[hash].Key) ? hash : hash + '-' + i; hashs[hash] = hashs[hash] || { Key: key, Elements: [] }; hashs[hash].Elements.push(valFunc(a[i])); } if (as_array) { var out = [], j = '', keys = Object.keys(hashs); for (var j = 0; j < keys.length; j++) out.push(hashs[keys[j]]); return out; } return hashs; }; function group_by_control(a, keyFunc, valFunc) { if (!a.length) return as_array ? [] : {}; var keyFunc = keyFunc || function (e) { return e; }, valFunc = valFunc || function (e) { return e; }; var hashs = {}, key = '', hash = ''; for (var i = 0; i < a.length; i++) { key = keyFunc(a[i]); hashs[key] = hashs[key] || { Key: key, Elements: [] }; hashs[key].Elements.push(valFunc(a[i])); } var out = [], j = '', keys = Object.keys(hashs); for (var j = 0; j < keys.length; j++) out.push(hashs[keys[j]]); return out; }; setup_hashers(); console.log(group_by_control( arr, function(e) { return e.N }, function(e) { return e.Name } )); console.log(group_by( arrComplex, function(e) { return e.N; }, function(e) { return e.Name; }, { Equals: function(a, b) { return a.Value == b.Value }, Hash: function(e) { return e.GetHashCode(); } } )); console.log(group_by( arrComplex, function(e) { return e.N; }, function(e) { return e.Name; }, { Equals: function(a, b) { return a.Value == b.Value }, Hash: function(e) { return e.GetHashCode(); } }, true ));