2012-01-19 9 views
40
this.col = Backbone.Collection.extend({ 
    model: M, 
    comparator: function(item) { 
     return item.get("level"); 
    } 
    }); 

Este código anterior ordena los elementos por nivel. Quiero ordenar por nivel, luego por título. ¿Puedo hacer eso? Gracias.comparador de la colección Backbone.js ordena por campos múltiples?

+3

Compatibilidad con los comparadores de estilo de clasificación desde principios de 2012. Solo acepte 2 argumentos y devuelva -1, 0 o 1. https: // github.com/documentcloud/backbone/commit/6b3ff7b0359510917d9ead8c8e16a7457eef05ef – geon

Respuesta

72

@ respuesta de amchang87 definitivamente las obras, pero sí que he encontrado trabajadas es simplemente devuelve una matriz de los campos que se pueden ordenar:

no he probado esto en varios navegadores sin embargo, como creo que se basa en JS 'comportamiento en orden de clasificación para matrices (en función de su contenido). Definitivamente funciona en WebKit.

+5

Existe soporte para comparadores de estilo de clasificación desde el comienzo de 2012. Simplemente acepte 2 argumentos y devuelva -1, 0 o 1. https://github.com/documentcloud/backbone/commit/6b3ff7b0359510917d9ead8c8e16a7457eef05ef – geon

+2

Tan directo, esto técnica es. Aquí hay un ejemplo en CoffeeScript: https://gist.github.com/jhabdas/9822535 –

+5

Tenga en cuenta que (al menos en Webkit) los dos campos se compararán como cadenas. Si estás tratando de ordenar los números vas a terminar con 1, 10, 2, 20, 200, etc. – RobW

1

Lo principal es que el Backbone se clasifica por un único valor relativo de un elemento a otro. Por lo tanto, no es directamente posible ordenar dos veces en una sola colección, pero probaría esto.

this.col = Backbone.Collection.extend({ 
    model: M, 
    comparator: function(item) { 
     // make sure this returns a string! 
     return item.get("level") + item.get("title"); 
    } 
}); 

Lo que esto hará es devolver una cadena de como "1 refrigerador", "1title", "2newTitle" ... Javascript debe ordenar las cuerdas por el carácter numérico primero y luego cada personaje después. Pero esto solo funcionará siempre que tus niveles tengan la misma cantidad de dígitos. IE "001title" vs "200title". Sin embargo, la idea principal es que necesita producir dos objetos comparables, alinear un número o cadena, que se puedan comparar entre sí según un criterio.

Otra solución sería utilizar guión bajo para "agrupar" su nivel y luego usar "sortby" para ordenar manualmente cada grupo de niveles y luego reemplazar manualmente la colección subyacente con esta matriz recién creada. Probablemente pueda configurar una función para hacer esto siempre que la colección "cambie".

+0

Si entiendo el método 'groupby' que arruinaría mi modelado de datos, ¿no? La primera solución es un poco agradable, así que gracias por eso. – Harry

+0

No estoy seguro de que esta sea la mejor manera de hacerlo. Por ejemplo, si tiene 2 modelos: modelo1-> nivel: 1, título: "1-cosa"; model2 -> level: 10, title: "-thing", entonces esperarías que fuera ordenado model1, model2 - pero por ese comparador, estaría comparando string "11-thing" y "10-thing", por lo que ordenados en el orden incorrecto –

+0

Esto es funcional, idéntico a tratar de ordenar por una matriz, devolviendo '[1, 'x']' desde un solo argumento, la función de comparador ordenará por la cadena '" 1, x "' en lugar de comparar los elementos. –

7

La concatenación de cadenas funciona bien al ordenar múltiples campos en orden ascendente, pero no funcionó porque 1) tuve que soportar asc/desc por campo y 2) ciertos campos eran campo de número (es decir, quiero 10 para venir después de 2 si está ascendiendo). Entonces, a continuación, una función de comparación que utilicé y funcionó bien para mis necesidades. Asume que la colección principal tiene una variable asignada con 'sortConfig', que es una matriz de objetos JSON con el nombre del campo y la dirección del orden de clasificación. Por ejemplo,

{ 
    "sort" : [ 
     { 
      "field": "strField", 
      "order": "asc" 
     }, 
     { 
      "field": "numField", 
      "order": "desc" 
     }, 
     ... 
    ] 
} 

Con el objeto JSON anteriormente asignado como 'sortConfig' a ​​la colección, la función a continuación hará Backbone ordenar por strField en orden ascendente primero, a continuación, ordenar por NumField en orden descendente, etc. Si no hay se especifica el orden de clasificación, ordena de forma ascendente por defecto.

multiFieldComparator: function(one, another) { 
    // 'this' here is Backbone Collection 
    if (this.sortConfig) { 
     for (var i = 0; i < this.sortConfig.length; i++) { 
      if (one.get(this.sortConfig[i].field) > another.get(this.sortConfig[i].field)) { 
       return ("desc" != this.sortConfig[i].order) ? 1 : -1; 
      } else if (one.get(this.sortConfig[i].field) == another.get(this.sortConfig[i].field)) { 
       // do nothing but let the loop move further for next layer comparison 
      } else { 
       return ("desc" != this.sortConfig[i].order) ? -1 : 1; 
      } 
     } 
    } 
    // if we exited out of loop without prematurely returning, the 2 items being 
    // compared are identical in terms of sortConfig, so return 0 
    // Or, if it didn't get into the if block due to no 'sortConfig', return 0 
    // and let the original order not change. 
    return 0; 
} 
2

Retornando una matriz no es coherente si es necesario en orden descendente y ascendente ... algunos

he creado un pequeño conjunto de funciones que se pueden utilizar para devolver el número entero comparación relevante de nuevo a Comparador Backbone función:

backbone-collection-multisort

0

"inspirado" en respuesta hyong.

Esto también le permite cambiar los datos antes de compararlos, valueTransforms es un objeto, si hay un atributo en ese objeto que tiene una función, se usará.

/* 
    * @param {Object} sortOrders ie: 
    * { 
    *  "description": "asc", 
    *  "duedate": "desc", 
    * } 
    * @param {Object} valueTransforms 
    */ 
    setMultiFieldComparator: function(sortOrders, valueTransforms) { 
     var newSortOrders = {}, added = 0; 
     _.each(sortOrders, function(sortOrder, sortField) { 
      if (["asc", "desc"].indexOf(sortOrder) !== -1) { 
       newSortOrders[sortField] = sortOrder; 
       added += 1; 
      } 
     }); 
     if (added) { 
      this.comparator = this._multiFieldComparator 
       .bind(this, newSortOrders, valueTransforms || this.model.prototype.valueTransforms || {}); 
     } else { 
      this.comparator = null; 
     } 
    }, 

    _multiFieldComparator: function(sortOrders, valueTransforms, one, another) { 
     var retVal = 0; 
     if (sortOrders) { 
      _.every(sortOrders, function(sortOrder, sortField) { 
       var oneValue = one.get(sortField), 
        anotherValue = another.get(sortField); 
       if (valueTransforms[sortField] instanceof Function) { 
        oneValue = valueTransforms[sortField](oneValue); 
        anotherValue = valueTransforms[sortField](anotherValue); 
       } 
       if (oneValue > anotherValue) { 
        retVal = ("desc" !== sortOrder) ? 1 : -1; 
       } else if (oneValue < anotherValue) { 
        retVal = ("desc" !== sortOrder) ? -1 : 1; 
       } else { 
        //continue 
        return true; 
       } 
      }); 
     } 
     return retVal; 
    }, 
Cuestiones relacionadas