1

My problem is pretty simple:

  • I have a lot of user scores in my DataBase (I'm using Firebase by the way)
  • I would like to get these scores (it's easy) and sort them (less easy)

If the user finish the workout, he will enter his time, if not, he will enter the number or repetitions.

To be simple, the result object is like that:

Result
 |_ resultValue (Int) // It can be time in seconds or number of reps
 |_ isFinished (Int) // It can be 0 (not finished) or 1 (finished)

I would like to sort my results array like that:

  1. First: All results with isFinished == 1 are higher than the others
  2. Then: Sort results where isFinished == 1 by resultValue DESC
  3. Finally: Sort results where isFinished == 0 by resultValue ASC

I tried something like that:

self.results = self.results.sorted {
 ($0.result.isForTimeFinished!, $0.result.resultValue!) > 
 ($1.result.isForTimeFinished!, $1.result.resultValue!)
}

So results of workout not finished are after results of workout finished, but "Sort results where isFinished == 1 by resultValue DESC" is not okay...

Do you know how I could combine all that?

3 Answers 3

1

Try this:

self.results = self.results.sorted {
    if $0.result.isForTimeFinished != $1.result.isForTimeFinished {
        return $0.result.isForTimeFinished > $1.result.isForTimeFinished
    }
    else if $0.result.isForTimeFinished == 0 {
        return $0.result.resultValue > $1.result.resultValue
    }
    else {
        return $0.result.resultValue < $1.result.resultValue
    }
}

Note: I might have got the signs the other way around but play around a bit

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

Comments

1

Logical OR stops at first TRUE evaluation thus no need to evaluate next expression. There are 3 rules when $0 should be above $1 in the list, try next:

self.results = self.results.sorted {
    let b0 = $0.result.isForTimeFinished!
    let b1 = $1.result.isForTimeFinished!
    let v0 = $0.result.resultValue!
    let v1 = $1.result.resultValue!
    // $0 will be above $1 in the list if returning true, otherwise it will be listed lower
    return 
        b0 && !b1 // is finished > not finished
        ||
        b0 && b0 == b1 && v0 < v1 // both finished, higher value
        ||
        !b0 && b0 == b1 && v0 > v1 // both not finished, lower value
}

Comments

0

I personally would add a computed var to your result model, called something like searchScore, which would be computed (I'm not quite following your goal though, but it looks like basically breaking your data into 3 strata and then playing with ascending/descending) which you could just then do a simple sort on.

So something like this

class Result {
...
    var searchScore: Double {
       return *ALGORITHM FOR SCORE*
    }
}
let sorted = results.sorted {$0.searchScore < $0.searchScore}

The real plus side of this approach is keeping your code cleaner, and not having to have real complexity of how to order things in the wrong place.

1 Comment

It's pretty simple, I'm sorry if I'm not clear. If the user finish the workout, he will be better than users that don't finish their workouts. Now if he finish the workout, the value retained will be the time, and the idea will be to have the shorter time as possible to rank higher. On the other hand, if a user doesn't finish the workout he will log his number of reps, and the idea will be to have as many reps as possible to rank higher (but it any cases, he will be after users that finish the workout)

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.