6

I am trying to sort an array like this (my state):

[
  {
    name:"Aenean in justo ante"
  },
  {
    name:"Phasellus viverra mattis dolor"
  }
]

I dispatch an action to the reducer: (part of reducer)

case 'SORT_COLLECTION':
  return state.sort(function(a, b) {
    var nameA = a.name.toLowerCase(), nameB = b.name.toLowerCase();
    if (nameA < nameB) {
      return -1;
    }
    if (nameA > nameB) {
      return 1;
    }
    return 0;
  })

but it does not work. Could somebody tell me where the mistake is?

3
  • 3
    Sort will mutate the array there Commented Aug 30, 2017 at 15:01
  • Reducer should not mutate a state. It must return a new state object and leave the previous one untouched. If you want to have sorting option, you have to add respective property (e.g. specifying sorting direction) to your state and sort data in render() depending on that property's value. Commented Aug 30, 2017 at 15:11
  • What exactly do you mean by "it doesnt work". This code doesn't contain any obvious bugs. Commented Aug 30, 2017 at 23:17

4 Answers 4

18

The sorting function should work fine. But you should not mutate the original state in the reducer. You can create a copy of the state array by calling state.slice() before sorting.

case 'SORT_COLLECTION':
  return state.slice().sort(function(a, b) {
    var nameA = a.name.toLowerCase(),
      nameB = b.name.toLowerCase()
    if (nameA < nameB)
      return -1
    if (nameA > nameB)
      return 1
    return 0
  })

Of course, you can define a simpler sort function as well.

const state = [{name:'foo'},{name:'bar'},{name:'baz'}]
const sortByKey = key => (a, b) => a[key] > b[key] ? 1 : -1
const sorted = state.slice().sort(sortByKey('name'))
console.log(`state=${JSON.stringify(state)}\nsorted=${JSON.stringify(sorted)}`)

Sign up to request clarification or add additional context in comments.

2 Comments

Array.slice does not do a deep copy, hence if you have a nested data structure you're still referencing original state
@JoeSeifi Yes. The state of the child objects has not changed, only their order. There's no reason to prefer deep copying here.
4

You need to do:

state.slice().sort(...

As sort() changes the original array by reordering references (mutates it) which is a "no go" for redux store. slice() first does a shallow copy meaning only references are copied and it is fast (unless it contains primitives in which case they will be copied, but it will still be fast) and then those new references are moved around by sort().

NOTE: you still can not change the objects within the array, but luckily sort does not change them.

Comments

0

if you are using es6 you can try like this state.sort((a,b) => a.name - b.name);

Comments

0

The Array.prototype.sort method requires you to return an integer or a boolean.

The below shows how to order in either direction.

var arr = [
        {
            name:"Aenean jon justo ante"
        },
        {
            name:"Aenean in justo ante"
        },
        {
            name:"Phasellus viverra mattis dolor"
        }
]

console.log("Alphabetical:", arr.sort((a,b) => a.name > b.name));
console.log("Reversed:", arr.sort((a,b) => a.name < b.name));

1 Comment

Have you read the article you linked? compareFunction may return any number, not true or false.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.