Esquema

var my_array = [ 
{
    "title": "a",
    "pinned": {
        "status": "true",
        "order": "1"
    }
},
{
    "title": "d",
    "pinned": {
        "status": "false",
        "order": "n/a"
    }
},
{
    "title": "c",
    "pinned": {
        "status": "true",
        "order": "0"
    }
},
{
    "title": "b",
    "pinned": {
        "status": "false",
        "order": "n/a"
    }
}
];

Comportamiento deseado

Ordenar objetos por title alfabéticamente,
a menos que tengan un estado pinned de true,
en cuyo caso mover este "subconjunto" de elementos al comienzo de la matriz,
ordenados por su valor pinned.order.

Un escenario de ejemplo sería un foro que tenía publicaciones ordenadas por fecha, pero también tenía publicaciones fijas en la parte superior, que estaban ordenadas por un orden definido.

El esquema original, por lo tanto, se mostrará como:

[ 
{// i am at the top, because i have a pinned status of true and order of 0  
    "title": "c",
    "pinned": {
        "status": "true",
        "order": "0"
    }
},
{// i am second from the top, because i have a pinned status of true and order of 1  
    "title": "a",
    "pinned": {
        "status": "true",
        "order": "1"
    }
},
{// i follow in alphabetical order 
    "title": "b",
    "pinned": {
        "status": "false",
        "order": "n/a"
    }
},
{// i follow in alphabetical order 
    "title": "d",
    "pinned": {
        "status": "false",
        "order": "n/a"
    }
}
]

Lo que he probado

my_array.sort(function(a, b) {
    return a.pinned.order.localeCompare(b.pinned.order) || a.title.localeCompare(b.title);
});

Basado en esta respuesta:

https://stackoverflow.com/a/45741804

También probé ...

Pensé en crear dos matrices separadas basadas en el valor de pinned.status, ordenarlas por separado y luego recombinarlas (como se muestra a continuación), pero me pregunto si hay algo más elegante.

var my_array = [ 
{
    "title": "a",
    "pinned": {
        "status": "true",
        "order": "1"
    }
},
{
    "title": "d",
    "pinned": {
        "status": "false",
        "order": "n/a"
    }
},
{
    "title": "c",
    "pinned": {
        "status": "true",
        "order": "0"
    }
},
{
    "title": "b",
    "pinned": {
        "status": "false",
        "order": "n/a"
    }
}
];


var my_subset = [];

for (var i = 0; i < my_array.length; i++) {

    if (my_array[i].pinned.status === "true") {
        // add to subset
        my_subset.push(my_array[i]);
        // remove from original array
        my_array.splice(i, 1);
    }

}

// sort "pruned" original array alphabetically
my_array.sort(function(a, b) {
    return a.title.localeCompare(b.title);
});

// sort subset array by pinned.order
my_subset.sort(function(a, b) {
    return a.pinned.order.localeCompare(b.pinned.order, undefined, { numeric: true });
});

// prepend subset to original array
var new_array = my_subset.concat(my_array);

// array is sorted as desired
console.log(new_array);
0
user1063287 8 sep. 2018 a las 12:24

3 respuestas

La mejor respuesta

Primero arregle los datos haciendo que la cadena numérica sea un número y la cadena booleana un booleano:

for (const item of my_array) {
    item.pinned.status = JSON.parse(item.pinned.status);
    item.pinned.order = Number(item.pinned.order);
}

Ahora no tendrá que compararlos como cadenas. De lo contrario, su enfoque es básicamente bueno, simplemente olvidó el indicador más significativo de si un elemento debe ir a la cima o no: es pinned.status. Compare por eso primero, de modo que cualquier elemento anclado venga antes que cualquier elemento no anclado.

my_array.sort(function(a, b) {
    return -(a.pinned.status - b.pinned.status) // reverse: true before false
    || (a.pinned.status // equal to b.pinned.status
      ? a.pinned.order - b.pinned.order
      : a.title.localeCompare(b.title));
});
var my_array = [{
    "title": "a",
    "pinned": {
      "status": true,
      "order": 1
    }
  },
  {
    "title": "d",
    "pinned": {
      "status": false,
      "order": 0
    }
  },
  {
    "title": "c",
    "pinned": {
      "status": true,
      "order": 0
    }
  },
  {
    "title": "b",
    "pinned": {
      "status": false,
      "order": 0
    }
  }
];

my_array.sort(function(a, b) {
  return -(a.pinned.status - b.pinned.status) // reverse: true before false
    ||
    (a.pinned.status // equal to b.pinned.status
      ?
      a.pinned.order - b.pinned.order :
      a.title.localeCompare(b.title));
});

console.log(my_array);

También puedes hacer

my_array.sort(function(a, b) {
    return -(a.pinned.status - b.pinned.status) // reverse: true before false
    || a.pinned.order - b.pinned.order
    || a.title.localeCompare(b.title);
});

Como elementos no anclados tienen el mismo orden (NaN) pero el primero es más explícito.

2
user1063287 8 sep. 2018 a las 09:58

Solo intenta esto:

my_array.sort(function(a, b) {
    return a.title.localeCompare(b.title);
}).sort(function(a, b) {
    return a.pinned.order.localeCompare(b.pinned.order)
});
1
Ankit 8 sep. 2018 a las 10:13

Si puede usar Lodash (biblioteca de utilidades para Javascript) puede usar orderBy u sortBy:

Usa Lodash en el proyecto:

<script src="lodash.js"></script>

Use orderBy para ordenar:

_.orderBy(my_array, [function(e) { return e.pinned.status}, 'title'], ['asc', 'asc']);

Más información sobre pedido

0
Saeed 8 sep. 2018 a las 10:01