0

我正在尝试通过对另一个对象的引用对 JavaScript 数组进行排序。

我有一组网格。每个网格都包含一个名为“纹理”的属性,其中包含对 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;
    }
});
4

5 回答 5

1

可能不是最好的解决方案,但它可以工作(使用纯 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++;
    }
});
于 2013-10-02T11:54:11.247 回答
1

我怀疑是否有内置的解决方案。

除非您可以在对象上定义排序关系,否则您不能使用Array.sort(),例如实现操作<=。JavaScript 不会暴露内存地址等元信息,因此没有不显眼的解决方案。

与往常一样,“快速”是一个相对术语。没有“绝对快”,只有“足够快”,所以一个简单的O(n^2)实现可能对你有用。

于 2013-10-02T11:33:54.647 回答
1

这是使用新的实验性WeakMap进行排序的一种方法(在撰写本文时仅在 Firefox 中支持)。通过在 WeakMap 的帮助下将每个纹理与索引相关联,我们可以在排序函数中使用索引:

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);
}

http://jsfiddle.net/ch5QJ/

我不知道这有多快,正如我所说,只有 Firefox 支持 WeakMap。但它可能值得对其进行测试,并将其用于支持它的浏览器,如果它确实很快的话。

于 2013-10-02T12:03:32.417 回答
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));

jsFiddle

于 2013-10-02T13:21:00.737 回答
0

您可以为此使用下划线。无论如何,如果您需要操纵对象,我建议您包括在内,让您的生活更轻松:

这里有小提琴:http: //jsfiddle.net/pmcalabrese/2KSSn/

这里是代码。

sorted = _(meshes).sortBy(function(meshes) {
    return meshes.texture;
});

我建议你看一下 sortBy 的文档。

于 2013-10-02T11:27:39.173 回答