Home > Back-end >  Sort string array with exceptions Swift
Sort string array with exceptions Swift

Time:12-17

I want to sort an array of string alphabetically, but with an exception that some elements should always be sorted first and second in the array. Here are the elements of the array:

["cat", "dog", "bird", "zebra", "elephant"]

I want it sorted alphabetically, but with zebra always first and cat always second, so after sorted it should look like:

["zebra", "cat", "bird", "dog", "elephant"]

This is how I have approached it:

    let animals = ["cat", "dog", "bird", "zebra", "elephant"]
    
    animals = animals.sorted(by: { first, second in
        if first == "zebra" {return true}
        if first == "cat" {return true}
        return first < second
    })

It returns zebra first, but not cat second

CodePudding user response:

The closure you pass to sorted(by:) should return true whenever the elements are in ascending order, and false otherwise. You can’t tell if the elements are in ascending order unless you check both the first value and the second. That’s why returning true whenever the first value is “zebra” or “cat” won’t work—not all your bases are covered.

One solution would be to use a switch statement to specify the comparison logic depending on the values you’re looking at:

let animals = ["cat", "dog", "bird", "zebra", "elephant"].sorted {
    switch ($0, $1) {
    case ("zebra", "cat"): // zebra is before cat
        return true
    case ("cat", "zebra"): // cat is not before zebra
        return false
    case ("cat", _), ("zebra", _): // zebra/cat are before everything
        return true
    case (_, "cat"), (_, "zebra"): // zebra/cat are not after anything
        return false
    case let (lhs, rhs): // alphabetical order
        return lhs < rhs
    }
}
// ["zebra", "cat", "bird", "dog", "elephant"]

If this looks a little overengineered, that’s because it is. Covering all your bases like this is hard, so I would definitely recommend taking a look at your use case and considering whether you really need to do it this way. If you can get away with something simpler, that’s probably your best bet. For example:

let animals = ["zebra", "cat"]   ["dog", "bird", "elephant"].sorted()
// ["zebra", "cat", "bird", "dog", "elephant"]

Or, if the animals array can’t be modified, another option would be to hard-code the exceptions:

let exceptions = ["zebra", "cat"]
let otherAnimals = animals.filter { !exceptions.contains($0) }.sorted()
let sortedResult = exceptions   otherAnimals
// ["zebra", "cat", "bird", "dog", "elephant"]

Edit: A now-removed comment questioned the reliability of the switch statement method. I tested it with every possible order of the animals array, and it returned the correct result every time.

CodePudding user response:

The answer you're going for is:

var animals = ["cat", "dog", "bird", "zebra", "elephant"]

animals = animals.sorted(by: { first, second in
    if first == "cat" && second == "zebra" {return false;}
    if second == "cat" && first == "zebra" {return true;}                          
    if first == "zebra" || first == "cat" {return true;}
    if second == "zebra" || second == "cat" {return false;}
    
    return first < second
})

You could even try calling animals.shuffle() before you sort to check that the sorting works regardless of input ordering.

This is the more readable way to do it, although potentially less efficient:

var animals = ["cat", "dog", "bird", "zebra", "elephant"]

animals = animals.sorted();

var wordIndex:Int = animals.firstIndex(where: {$0 == "zebra"})!;
animals.insert(animals.remove(at: wordIndex), at: 0);

wordIndex = animals.firstIndex(where: {$0 == "cat"})!;
animals.insert(animals.remove(at: wordIndex), at: 1);

Or if you want a more generalized version:

 func sortAndPrepend(inputArr: [String], prependWords: [String]) -> [String]{
    var array = inputArr.sorted();
    
    var wordIndex:Int;
    for i in 0...prependWords.count-1 {
        wordIndex = array.firstIndex(where: {$0 == prependWords[i]}) ?? -1;
        if (wordIndex != -1) {
            array.insert(array.remove(at: wordIndex), at: i);
        }
    }
    return array;
}
  • Related