我正在尝试通过引用另一个对象来对JavaScript数组进行排序。
我有一个Mesh数组。每个网格都包含一个名为“纹理”的属性,该属性包含对WebGLTexture对象的引用。
WebGLTexture对象不包含任何可读属性,我只能通过引用(==
)进行比较。没有定义toString方法。
以下是初始情况的示例:
var texture1 = gl.createTexture(/* blah */); // Returns a WebGLTexture object
var texture2 = gl.createTexture(/* blah */); // Returns a WebGLTexture object
var texture3 = gl.createTexture(/* blah */); // Returns a WebGLTexture object
var meshes = [
{name: "Mesh 0", texture: texture1},
{name: "Mesh 1", texture: texture2},
{name: "Mesh 2", texture: texture3},
{name: "Mesh 3", texture: texture3},
{name: "Mesh 4", texture: texture2},
{name: "Mesh 5", texture: texture1},
{name: "Mesh 6", texture: texture1},
{name: "Mesh 7", texture: texture2},
{name: "Mesh 8", texture: texture3},
{name: "Mesh 9", texture: texture1}
];
我想要做的是通过纹理参考对数组进行排序,得到类似的东西(顺序并不重要,我只想让具有相同纹理的对象连续):
var meshes = [
{name: "Mesh 0", texture: texture1},
{name: "Mesh 5", texture: texture1},
{name: "Mesh 6", texture: texture1},
{name: "Mesh 9", texture: texture1},
{name: "Mesh 1", texture: texture2},
{name: "Mesh 4", texture: texture2},
{name: "Mesh 7", texture: texture2},
{name: "Mesh 2", texture: texture3},
{name: "Mesh 3", texture: texture3},
{name: "Mesh 8", texture: texture3}
];
我知道可以通过循环来实现它,但它需要创建对象和数组,并执行许多嵌套循环。性能在这里非常重要。
我能找到的最佳解决方案是为每个纹理手动添加一个唯一的“id”属性,并在其上使用Array.sort。但我对这个修改原生对象的解决方案并不满意。
你知道任何原生和快速的方法吗?
编辑:根据誓言答案,这是解决方案:
var tempSortTextures = [];
meshes.sort(function(a, b) {
var iA = null;
var iB = null;
for(var i = 0 ; i <= tempSortTextures.length ; i++) {
if(i == tempSortTextures.length) {
if(iA == null) {
tempSortTextures.push(a.texture);
} else /*if(iB == null)*/ {
tempSortTextures.push(b.texture);
}
}
var currentTexture = tempSortTextures[i];
if(iA == null && a.texture == currentTexture) iA = i;
if(iB == null && b.texture == currentTexture) iB = i;
if(iA != null && iB != null) return iA - iB;
}
});
答案 0 :(得分:1)
我怀疑是否有内置的解决方案。
除非您可以在对象上定义排序关系,否则无法使用Array.sort()
,例如实施一项行动<=
。 JavaScript不会暴露内存地址等元信息,因此没有非突兀的解决方案。
与往常一样,“快速”是一个相对术语。没有“绝对快速”,只有“足够快”,所以天真的O(n^2)
实现可能适合你。
答案 1 :(得分:1)
这是使用新实验WeakMap进行排序的一种方法(在撰写本文时仅在Firefox中支持)。通过在WeakMap的帮助下将每个纹理与索引相关联,我们可以使用sort函数中的索引:
var texture1 = {name:"tex1"};
var texture2 = {name:"tex2"};
var texture3 = {name:"tex3"};
var meshes = [
{name: "Mesh 0", texture: texture1},
{name: "Mesh 1", texture: texture2},
{name: "Mesh 2", texture: texture3},
{name: "Mesh 3", texture: texture3},
{name: "Mesh 4", texture: texture2},
{name: "Mesh 5", texture: texture1},
{name: "Mesh 6", texture: texture1},
{name: "Mesh 7", texture: texture2},
{name: "Mesh 8", texture: texture3},
{name: "Mesh 9", texture: texture1}
];
var textureSortOrder = [texture1, texture2, texture3];
var sortMap = new WeakMap();
for (var i=0;i<textureSortOrder.length;i++) {
sortMap.set(textureSortOrder[i], i);
}
meshes = meshes.sort(function(a, b){
var indexA = sortMap.get(a.texture);
var indexB = sortMap.get(b.texture);
if (indexA < indexB) {
return -1;
} else if (indexA > indexB) {
return 1;
} else {
return 0;
}
});
console.log("sorted:");
for (var i=0;i<meshes.length;i++) {
console.log(i, meshes[i].texture.name);
}
我不知道这有多快,正如我所说,只有Firefox支持WeakMap。但它可能值得测试它并将其用于支持它的浏览器,如果它确实变得很快。
答案 2 :(得分:0)
您可以使用下划线。无论如何,我建议你包括如果你需要操纵物体,让你的生活更轻松:
这里有小提琴:http://jsfiddle.net/pmcalabrese/2KSSn/
这里是代码。
sorted = _(meshes).sortBy(function(meshes) {
return meshes.texture;
});
我建议你看一下sortBy的文档。
答案 3 :(得分:0)
可能不是最好的解决方案,但它有效(使用纯javascript):)
var map = [texture1, texture2, texture3];
meshes.sort(function (a, b) {
var i = 0, item;
while (item = map[i]) {
if (a.texture && item === a.texture) { a = i; }
if (b.texture && item === b.texture) { b = i; }
if (!a.texture && !b.texture) { return a - b; }
i++;
}
});
答案 4 :(得分:0)
也许是这样的?我还没有做过任何性能测试。
的Javascript
function sortByTextureOrderOrGroupThem (theArray, theOrder) {
var temp = [],
thisOrder = theOrder || [],
thisOrderLength = thisOrder.length,
thisOrderIndex,
order,
theArrayLength,
theArrayIndex,
element;
// if we were given an order then build the temp array from that and remove the element from theArray
for (thisOrderIndex = 0; thisOrderIndex < thisOrderLength; thisOrderIndex += 1) {
order = thisOrder[thisOrderIndex];
for (theArrayIndex = theArray.length - 1; theArrayIndex >= 0; theArrayIndex -= 1) {
element = theArray[theArrayIndex];
if (element.texture === order) {
temp.push(theArray.splice(theArrayIndex, 1)[0]);
}
}
}
// anything remaining in the array, group them together
theArray.sort(function (a, b) {
if (a.texture === b.texture) {
return 0;
}
if (a.texture < b.texture) {
return -1;
}
return 1;
});
// join any remaining grouped items to the temp
temp = temp.concat(theArray);
// empty theArray
theArray.length = 0;
// add the length and the starting index for use with apply
temp.unshift(temp.length);
temp.unshift(0);
// splice temp back into theArray
[].splice.apply(theArray, temp);
// return theArray in case it is needed this way
return theArray;
}
var texture1 = {"name": "texture1"},
texture2 = {"name": "texture2"},
texture3 = {"name": "texture3"},
meshes = [
{name: "Mesh 0", texture: texture1},
{name: "Mesh 1", texture: texture2},
{name: "Mesh 2", texture: texture3},
{name: "Mesh 3", texture: texture3},
{name: "Mesh 4", texture: texture2},
{name: "Mesh 5", texture: texture1},
{name: "Mesh 6", texture: texture1},
{name: "Mesh 7", texture: texture2},
{name: "Mesh 8", texture: texture3},
{name: "Mesh 9", texture: texture1}
],
order = [texture1, texture2, texture3];
sortByTextureOrderOrGroupThem(meshes, order);
console.log(JSON.stringify(meshes));