在Javascript中实现GroupBy最有效的方法是什么?
我试图用这些参数实现一个在Javascript中实现GroupBy最有效的方法是什么?,javascript,performance,grouping,Javascript,Performance,Grouping,我试图用这些参数实现一个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 }, GetHas
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和我创建的一个方法,它不使用比较器,只处理平面类型。()
下划线和Lo Dash等其他库不接受比较器
参数。因此,它们的实现是不相关的
我的键可以是一个类,所以我需要一些东西来确定TKey
在不同的实例中是否相同
所以基本上我想做的就是复制C#LinqGroupBy
行为
样本输入:
var arrComplex =
[
{ N: { Value: 10 }, Name: "Foo" },
{ N: { Value: 10 }, Name: "Bar" },
{ N: { Value: 20 }, Name: "Foo" },
{ N: { Value: 20 }, Name: "Bar" }
];
示例输出(或类似的内容):
有没有关于如何实施的想法
赏金
对于赏金,我希望你考虑到:
- 键可以是一个对象
- 如果某个属性相等,则两个对象可以相等
- 它应该与现有解决方案一样快或比现有解决方案更快
- 结果可以是一个数组或对象,只要我能得到按键分组的元素就行了
好吧,我希望得到一个完整的答案。我设法以这种方式实现了: 我需要从对象中获取哈希代码
Object.prototype.GetHashCode = function () {
var s = this instanceof Object ? stringify(this) : this.toString();
var hash = 0;
if (s.length === 0) return hash;
for (var i = 0; i < s.length; ++i) {
hash = ((hash << 5) - hash) + s.charCodeAt(i);
}
return hash;
};
Number.prototype.GetHashCode = function () { return this.valueOf(); };
对性能影响不大。对于大型物体,它是相同的,对于小型物体,它会丢失,但仍然是非常快速和安全的
我的分组方法
function GroupBy(a, keySelector, elementSelector, comparer)
{
// set default values for opitinal parameters
elementSelector = elementSelector || function(e) { return e; };
comparer = comparer ||
{
Equals: function(a,b) { return a==b },
GetHashCode: function(e) { return e.GetHashCode(); }
};
var key, hashKey, reHashKey;
// keep groups separated by hash
var hashs = {};
for (var i = 0, n = a.length; i < n; ++i)
{
// in case of same hash, but Equals returns false
reHashKey = undefined;
// grabs the key
key = keySelector(a[i]);
// grabs the hashcode
hashKey = comparer.GetHashCode(key);
// if a hash exists in the list
// compare values with Equals
// in case it return false, generate a unique hash
if (typeof hashs[hashKey] !== "undefined")
reHashKey = comparer.Equals(key, hashs[hashKey].Key) ? hashKey : hashKey + " " + i;
// if a new hash has been generated, update
if (typeof reHashKey !== "undefined" && reHashKey !== hashKey)
hashKey = reHashKey;
// get/create a new group and add the current element to the list
hashs[hashKey] = hashs[hashKey] || { Key: key, Elements: [] };
hashs[hashKey].Elements.push(a[i]);
}
return hashs;
}
现在是绝地武士
但是,我的GroupBy
实现比linq.js的GroupBy
慢。只有当我将转换为阵列()
时,它才会更快。也许linq.js只在我转换为数组时才真正执行,这就是为什么不同,我不确定这一部分
测试结果
Name op/s
---------------------------------
GroupBy 163,261
GroupByToArray 152,382
linq.js groupBy 243,547
linq.js groupBy toArray 26,309
我使用您的jsperf作为参考,了解脚本的一些细节。我真的,真的很喜欢你的“散列”代码,所以我完全把它偷走了。根据“browserscope”图表,Mine使用另一种方法生成用于生成哈希的字符串,这似乎快了一点,从而提高了性能。我在测试中包含了一个“太多递归”的概念证明,以表明它具有递归保护,如JSON.stringify和.toSource() 显示代码返回所需的格式。似乎表明它优于发布的解决方案。我还包括了linq.js解决方案,但对我来说,它在FireFox中的性能相当差。它在Safari、Chrome和IE中的运行速度相当,但除了IE之外,它的运行速度并不比我的快。我甚至在我的手机上试用过它,但我仍然有相同的性能差异。我个人在所有浏览器的最新版本中与发布的解决方案并排进行了测试,mine在每个浏览器中的性能都超过了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
));
var-arr=[
{N:10,名字:“Foo”},
{N:10,名称:“Bar”},
{N:20,名字:“Foo”},
{N:20,名字:“Bar”}
];
var poc={name:'blah',obj:{};
poc.obj=poc;
var arrComplex=[
{N:{Value:10,TooMuchRecursionProofPOC:poc},名称:“Foo”},
{N:{Value:10,TooMuchRecursionProofPOC:poc},名称:“Bar”},
{N:{Value:20,TooMuchRecursionProofPOC:poc},名称:“Foo”},
{N:{Value:20,TooMuchRecursionProofPOC:poc},名称:“Bar”}
];
var耳环=可枚举的起始值(arr);
var arrComplex=可枚举的源(arrComplex);
函数设置\u哈希器(){
//递归保护思想
var rp='_rp'+(Math.random()*10000000);
功能tstr(){
var out='',i='';
如果(this[rp]){this[rp]=未定义;返回;}
因为(我在这里)
如果(i!=rp&&this.hasOwnProperty(i))
out+=对象的此[i]实例
((this[rp]=true)&&this[i]!=this&&this[i][rp]?tstr.呼叫(this[i]):“”)
:(这个[i].toString | | tstr).呼叫(这个[i]);
返回;
};
Number.prototype.GetHashCode=函数(){
返回此.valueOf();
};
Object.prototype.GetHashCode=函数(){
var s=(对象的这个实例?tstr:this.toString | | tstr).call(this),
h=0;
如果(s.长度)
对于(变量i=0;i h=((h)您真的需要自己的实现吗?您不能使用下划线.js
或其他库吗?lodash.js
是另一种选择,它比下划线.js
@BrunoLM更快、更高效。为什么需要比较器
呢?如果比较器
返回两个元素的“相等”会发生什么具有不同键的ENT?此组将使用哪个键?能否添加一个示例,说明您需要比较器?以及预期的输出?喜欢这个问题。今天给了我一些要做的事情:)你将得到一份多么好的礼物啊。谢谢你喜欢代码。谢谢你理解我的问题。谢谢你提供有用的信息。节日快乐。
function GroupBy(a, keySelector, elementSelector, comparer)
{
// set default values for opitinal parameters
elementSelector = elementSelector || function(e) { return e; };
comparer = comparer ||
{
Equals: function(a,b) { return a==b },
GetHashCode: function(e) { return e.GetHashCode(); }
};
var key, hashKey, reHashKey;
// keep groups separated by hash
var hashs = {};
for (var i = 0, n = a.length; i < n; ++i)
{
// in case of same hash, but Equals returns false
reHashKey = undefined;
// grabs the key
key = keySelector(a[i]);
// grabs the hashcode
hashKey = comparer.GetHashCode(key);
// if a hash exists in the list
// compare values with Equals
// in case it return false, generate a unique hash
if (typeof hashs[hashKey] !== "undefined")
reHashKey = comparer.Equals(key, hashs[hashKey].Key) ? hashKey : hashKey + " " + i;
// if a new hash has been generated, update
if (typeof reHashKey !== "undefined" && reHashKey !== hashKey)
hashKey = reHashKey;
// get/create a new group and add the current element to the list
hashs[hashKey] = hashs[hashKey] || { Key: key, Elements: [] };
hashs[hashKey].Elements.push(a[i]);
}
return hashs;
}
var arrComplex =
[
{ N: { Value: 10 }, Name: "Foo" },
{ N: { Value: 10 }, Name: "Bar" },
{ N: { Value: 20 }, Name: "Foo" },
{ N: { Value: 20 }, Name: "Bar" }
];
//
var x = GroupBy(arrComplex
, function(e) { return e.N; }
, function(e) { return e.Name; }
, {
Equals: function(a,b) { return a.Value == b.Value },
GetHashCode: function(e) { return e.GetHashCode(); }
}
);
//
console.log(x);
Name op/s
---------------------------------
GroupBy 163,261
GroupByToArray 152,382
linq.js groupBy 243,547
linq.js groupBy toArray 26,309
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
));