简体   繁体   中英

Swift how to sort an array of custom objects in a given order with property

I have an array of restaurantsIds in a specific order. I make a network call to get Restaurants for each one of these ids. I am returned with all the Restaurants in the order that they are returned. Restaurants is a custom class and one of the properties is restaurantId. I want to sort the returned Restaurants in the order of the IDs. How would I do that? I think I am supposed to use the sorted function. How would I go upon doing this? There can be duplicate restaurants so I can't use a dictionary to do it. If is is a duplicate restaurant that is returned then it doesn't really matter which instance of that restaurants comes first.

var restaurantIds = [947575, 858914, 255964]
var returnedRestaurant:[Restaurant]!
returnedRestaurant.sorted { (rest1, rest2) -> Bool in
        //How would I go about matching rest1 and rest2 to the list of restaurantIds above?
        rest1.productID......
    }

If I understand correctly you wish to sort your array by the order the IDs occur in restaurantIds .

Array provides the method index(of:) which given an element returns the index of its first occurrence in the array. For example restaurantIds.index(of:858914) would return 1 .

You can use the result of index(of:) as your sorting key. Ie In your sorting function the value you need to compare for rest1 would be restaurantIds.index(of: rest1.productID) .

HTH

Addendum

@vacawama raises and issue with is both correct in principle but may be wrong in practice... Using restaurantIds.index(of: rest1.productID) a lot is not good for performance and for longer lists of restaurant IDs you may need to optimise by avoiding the repeated index(of:) calls. However for short lists any performance change (it could be a win or lose) is probably insignificant and optimisation just complicates the code.

TL;DR: YMMV, KISS, beware premature optimisation, your call.

Create a dictionary that maps the restaurantID to the order of that ID. You can then use that to sort your array:

var restaurantIds = [947575, 858914, 255964]

// Create a dictionary that maps the order of the restaurantID
var order = [Int: Int]()
for (idx, rid) in restaurantIds.enumerated() {
    order[rid] = idx
}

var returnedRestaurant:[Restaurant]!

returnedRestaurant.sort { (rest1, rest2) -> Bool in

    // look up the order for the restaurant id.  If the id is missing, use
    // Int.max to put that restaurant at the end of the array

    let order1 = order[rest1.id] ?? Int.max
    let order2 = order[rest2.id] ?? Int.max

    return order1 < order2
}

If you have more than just a few restaurant IDs, this is likely more efficient than repeatedly using index(of:) to figure out the mapping. The larger your restaurant array, the more times index(of:) is called (because sort is O(n log n) ) so the number of calls to index(of:) grows rapidly as the size of the restaurant array increases. index(of:) is an O(n) operation based upon the size of the id array. The larger your id array, the slower index(of:) becomes.

One way to do it is to iterate through your restaurantIds array using flatMap and return the first element from your returnedRestaurant array whose id matches the id at the current index in restaurantIds . This function returns a new sorted array and doesn't mutate the returnedRestaurant array in place.

let sortedRestaurants = restaurantIds.flatMap{ id in
    returnedRestaurant.filter{$0.restaurantId == id }.first
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM