Reputation: 1029
I want the user to rank an array of options into their liking. I have 1 array holding original values for Options, 1 array holding updated values from the original. How can I get the remaining options? I'm on Swift 4.2
My idea is to convert both into Sets and use symmetric difference.
options array provides the original choice
ranked array has value property of user's rank (1,2,3, or 4)
remainingOptions should contains fruits that are not ranked yet
However, I get duplicates.
Feel free to suggest other ways.
struct Option: Hashable {
var title: String
var key: String
var value: Int
}
var apple = Option(title: "Apple", key: "apple", value: 0)
var grape = Option(title: "Grape", key: "grape", value: 0)
var banana = Option(title: "Banana", key: "banana", value: 0)
var papaya = Option(title: "Papaya", key: "papaya", value: 0)
var options = [apple, grape, banana, papaya]
apple.value = 1
grape.value = 2
var ranked = [apple, grape]
let originalSet: Set<Option> = Set(options)
var rankedSet: Set<Option> = Set(ranked)
let remainingOptions = originalSet.symmetricDifference(rankedSet)
Result:
{title "Grape", key "grape", value 1}
{title "Apple", key "apple", value 0}
{title "Grape", key "grape", value 2}
{title "Banana", key "banana", value 0}
{title "Apple", key "apple", value 1}
{title "Papaya", key "papaya", value 0}
Wanted result:
{title "Banana", key "banana", value 0}
{title "Papaya", key "papaya", value 0}
Upvotes: 0
Views: 647
Reputation: 154671
The issue is that you are expecting two Option
s to be equal if they have the same title
and key
, but by default Swift is also checking the value
. Therefore, two Option
s with different value
s are considered different.
Symmetric difference returns a new set with the elements that are either in this set or in the given sequence, but not in both. Because you have changed the values, you end up with a union of your two sets because they have nothing in common.
You can fix this by explicitly implementing the hash(into:)
function and ==
functions to ignore the value
when checking for equality:
struct Option: Hashable, CustomStringConvertible {
var title: String
var key: String
var value: Int
var description: String { return "{title: \"\(title)\", key: \"\(key)\", value: \(value)}" }
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(key)
}
static func ==(lhs: Option, rhs: Option) -> Bool {
return lhs.title == rhs.title && lhs.key == rhs.key
}
}
var apple = Option(title: "Apple", key: "apple", value: 0)
var grape = Option(title: "Grape", key: "grape", value: 0)
var banana = Option(title: "Banana", key: "banana", value: 0)
var papaya = Option(title: "Papaya", key: "papaya", value: 0)
var options = [apple, grape, banana, papaya]
apple.value = 1
grape.value = 2
var ranked = [apple, grape]
let originalSet: Set<Option> = Set(options)
var rankedSet: Set<Option> = Set(ranked)
let remainingOptions = originalSet.symmetricDifference(rankedSet)
print(remainingOptions)
[{title: "Papaya", key: "papaya", value: 0}, {title: "Banana", key: "banana", value: 0}]
Note: symmetricDifference
takes a sequence, so it isn't necessary to convert ranked
into a Set
, you can just use the array:
let remainingOptions = originalSet.symmetricDifference(ranked)
Another Option: Use Filter
Instead of using Set
s and symmetricDifference
, you can use map
to get an array of keys
from the ranked
array, and then use filter
on the options
array to get the remaining
options that don't match those keys
:
let rankedKeys = ranked.map { $0.key }
let remaining = options.filter { !rankedKeys.contains($0.key) }
This doesn't require you to change the Option
struct from your original definition.
Upvotes: 1