Repository: vuejs/vue
Pull request: Multiple field order by

This is refers to the feature request #2518

I replaced the deconstructing array with a simple array because it makes it easier to use an array from data or computed properties: `files | orderBy sortKeys` instead of `files | orderBy 'name' 'size'` with `sortKeys = ['name', 'size']`. Tell me your thoughts about it


Changed files:
filename: src/filters/array-filters.js
patch: @@ -74,26 +74,39 @@ export function filterBy (arr, search, delimiter) {
 /**
  * Filter filter for arrays
  *
- * @param {String} sortKey
+ * @param {String|Array<String>} sortKeys
  * @param {String} reverse
  */
 
-export function orderBy (arr, sortKey, reverse) {
+export function orderBy (arr, sortKeys, reverse) {
   arr = convertArray(arr)
-  if (!sortKey) {
+  let order = (reverse && reverse < 0) ? -1 : 1
+
+  if (typeof sortKeys === 'string') {
+    sortKeys = [sortKeys]
+  } else if (!sortKeys || !sortKeys.length) {
     return arr
   }
-  var order = (reverse && reverse < 0) ? -1 : 1
-  // sort on a copy to avoid mutating original array
-  return arr.slice().sort(function (a, b) {
+
+  function compare (a, b, sortKeyIndex) {
+    const sortKey = sortKeys[sortKeyIndex]
     if (sortKey !== '$key') {
       if (isObject(a) && '$value' in a) a = a.$value
       if (isObject(b) && '$value' in b) b = b.$value
     }
     a = isObject(a) ? getPath(a, sortKey) : a
     b = isObject(b) ? getPath(b, sortKey) : b
     return a === b ? 0 : a > b ? order : -order
-  })
+  }
+
+  function recursiveCompare (a, b, i) {
+    i = i || 0
+    if (i === sortKeys.length - 1) return compare(a, b, i)
+    return compare(a, b, i) || recursiveCompare(a, b, i + 1)
+  }
+
+  // sort on a copy to avoid mutating original array
+  return arr.slice().sort(recursiveCompare)
 }
 
 /**

filename: test/unit/specs/filters/filters_spec.js
patch: @@ -204,6 +204,8 @@ describe('Filters', function () {
     // no sort key
     res = filter(arr, null)
     expect(res).toBe(arr)
+    res = filter(arr)
+    expect(res).toBe(arr)
   })
 
   it('orderBy on Object-converted array', function () {
@@ -227,6 +229,34 @@ describe('Filters', function () {
     res = filter(arr, 'v')
     assertArray(res, [arr[1], arr[2], arr[0]])
   })
+
+  it('orderBy multiple fields', function () {
+    var filter = filters.orderBy
+    var arr = [
+      { a: 1, b: 1, c: 1 }, // 0
+      { a: 0, b: 1, c: 1 }, // 1
+      { a: 1, b: 2, c: 0 }, // 2
+      { a: 1, b: 0, c: 0 }, // 3
+      { a: 0, b: 0, c: 0 }, // 4
+      { a: 0, b: 1, c: 0 }  // 5
+    ]
+    var res
+    // sort two keys
+    res = filter(arr, ['a', 'b'])
+    assertArray(res, [arr[4], arr[1], arr[5], arr[3], arr[0], arr[2]])
+
+    // sort two keys with order
+    res = filter(arr, ['a', 'b'], 1)
+    assertArray(res, [arr[4], arr[1], arr[5], arr[3], arr[0], arr[2]])
+
+    // sort three keys
+    res = filter(arr, ['a', 'b', 'c'])
+    assertArray(res, [arr[4], arr[5], arr[1], arr[3], arr[0], arr[2]])
+
+    // reverse two key. Preserves order when equal: 1 then 5
+    res = filter(arr, ['a', 'b'], -1)
+    assertArray(res, [arr[2], arr[0], arr[3], arr[1], arr[5], arr[4]])
+  })
 })
 
 function assertArray (res, expectations) {