Javascript - 按引用排序数组

时间:2013-10-02 11:05:34

标签: javascript arrays sorting opengl-es reference

我正在尝试通过引用另一个对象来对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;
    }
});

5 个答案:

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

http://jsfiddle.net/ch5QJ/

我不知道这有多快,正如我所说,只有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));

jsFiddle