Set (Collection) - Insert multiple elements
Asked Answered
B

4

50

Set is an unordered collection of unique elements. Almost similar to array.

I want to add/insert multiple elements in a Set of String. But there is only single method provided that can insert only one element (accepts single Set element as a parameter argument) and I've collection of string (id).

insert(_:)

@discardableResult mutating func insert(_ newMember: Set.Element) -> (inserted: Bool, memberAfterInsert: Set.Element)

How can I do that?

What I've tried:
I tried to create an extension very similar to insert(_:) method but it can accept multiple Set elements. It would be same as use of iteration over collection but don't need to handle it manually everywhere.

extension Set {

    @discardableResult mutating func insert(_ newMembers: [Set.Element]) -> (inserted: Bool, memberAfterInsert: Set.Element) {

        newMembers.forEach { (member) in
            self.insert(member)
        }
    }
}

It should work, if I return a tuple as expected but no idea how and where (which line) and what to return a value.

Here is error message.

Missing return in a function expected to return '(inserted: Bool, memberAfterInsert: Set.Element)'

enter image description here

What can be solution to this. Is there any better solution/approach to handle this operation?

Bicycle answered 15/2, 2018 at 18:38 Comment(3)
why do you want to return the tuple?Paduasoy
It is a default/actual definition of method insert(_:) for single Set element. I want to create a same function but it should accept multple Set elements.Bicycle
but what do you want the behavior to be? In what case would it return inserted: true? When one value is inserted? There is no "same function" that behaves differently. It's up to you to decide what the function returns. When all the values are inserted? What does your use case need that formUnion doesn't provide? In any case, as Milan notes, if you want to return that, you need to actually return that.Metalware
P
6

Your insert declaration states that the method is returning a tuple: (inserted: Bool, memberAfterInsert: Set.Element), but your method does not return anything.

Just use:

@discardableResult mutating func insert(_ newMembers: [Set.Element]) {

    newMembers.forEach { (member) in
        self.insert(member)
    }
}

UPDATE

The closest to get is this I believe:

extension Set {
    
    @discardableResult mutating func insert(_ newMembers: [Set.Element]) -> [(inserted: Bool, memberAfterInsert: Set.Element)] {
        var returnArray: [(inserted: Bool, memberAfterInsert: Set.Element)] = []
        newMembers.forEach { (member) in
            returnArray.append(self.insert(member))
        }
        return returnArray
    }
}

Reasoning:

The docs to the insert say:

Return Value

(true, newMember) if newMember was not contained in the set. If an element equal to newMember was already contained in the set, the method returns (false, oldMember), where oldMember is the element that was equal to newMember. In some cases, oldMember may be distinguishable from newMember by identity comparison or some other means.

E.g., for set {1, 2, 3} if you try to insert 2, the tuple will return (false, 2), because 2 was already there. The second item of the tuple would be object from the set and not the one you provided - here with Ints it's indistinguishable, since only number 2 is equal to 2, but depending on Equatable implementation you can have two different objects that would be evaluated as the same. In that case the second argument might be important for us.

Anyway, what I am trying to say is, that a single tuple therefore corresponds to a single newMember that you try to insert. If you try to insert multiple new members, you cannot describe that insertion just by using a single tuple - some of those new members might have already been there, thus for the the first argument would be false, some other members might be successfully inserted, thus for them the first argument of tuple would be true.

Therefore I believe the correct way is to return an array of tuples [(inserted: Bool, memberAfterInsert: Set.Element)].

Pomerania answered 15/2, 2018 at 18:42 Comment(1)
Nice.. Thank you for your answer but I did that. I want to make it very similar to actual definition of method insert(_:) for single Set element. I want to create a same function but it should accept multple Set elements.Bicycle
K
92

It was pointed out in the comments under the question, but I'd like to clearly state that there is a method for that very same purpose:

mutating func formUnion<S>(_ other: S) where Element == S.Element, S : Sequence

Usage:

var attendees: Set = ["Alicia", "Bethany", "Diana"]
let visitors = ["Diana", "Marcia", "Nathaniel"]
attendees.formUnion(visitors)
print(attendees)
// Prints "["Diana", "Nathaniel", "Bethany", "Alicia", "Marcia"]"

Source: Apple Developer


There is also an immutable variant which returns a new instance containing the union:

func union<S>(_ other: S) -> Set<Set.Element> where Element == S.Element, S : Sequence

Usage:

let attendees: Set = ["Alicia", "Bethany", "Diana"]
let visitors = ["Marcia", "Nathaniel"]
let attendeesAndVisitors = attendees.union(visitors)
print(attendeesAndVisitors)
// Prints "["Diana", "Nathaniel", "Bethany", "Alicia", "Marcia"]"

Source: Apple Developer

Ku answered 27/3, 2018 at 12:19 Comment(0)
C
22

Swift Set Union

[Swift Set operations]

a.union(b) - a ∪ b - the result set contains all elements from a and b

  • union - immutable function
  • unionInPlace(up to Swift v3) => formUnion - mutable function

[Mutable vs Immutable]

Read more here

Counterclockwise answered 30/4, 2019 at 9:8 Comment(1)
Thanks a lot! The formUnion function is my choice)Assimilation
P
6

Your insert declaration states that the method is returning a tuple: (inserted: Bool, memberAfterInsert: Set.Element), but your method does not return anything.

Just use:

@discardableResult mutating func insert(_ newMembers: [Set.Element]) {

    newMembers.forEach { (member) in
        self.insert(member)
    }
}

UPDATE

The closest to get is this I believe:

extension Set {
    
    @discardableResult mutating func insert(_ newMembers: [Set.Element]) -> [(inserted: Bool, memberAfterInsert: Set.Element)] {
        var returnArray: [(inserted: Bool, memberAfterInsert: Set.Element)] = []
        newMembers.forEach { (member) in
            returnArray.append(self.insert(member))
        }
        return returnArray
    }
}

Reasoning:

The docs to the insert say:

Return Value

(true, newMember) if newMember was not contained in the set. If an element equal to newMember was already contained in the set, the method returns (false, oldMember), where oldMember is the element that was equal to newMember. In some cases, oldMember may be distinguishable from newMember by identity comparison or some other means.

E.g., for set {1, 2, 3} if you try to insert 2, the tuple will return (false, 2), because 2 was already there. The second item of the tuple would be object from the set and not the one you provided - here with Ints it's indistinguishable, since only number 2 is equal to 2, but depending on Equatable implementation you can have two different objects that would be evaluated as the same. In that case the second argument might be important for us.

Anyway, what I am trying to say is, that a single tuple therefore corresponds to a single newMember that you try to insert. If you try to insert multiple new members, you cannot describe that insertion just by using a single tuple - some of those new members might have already been there, thus for the the first argument would be false, some other members might be successfully inserted, thus for them the first argument of tuple would be true.

Therefore I believe the correct way is to return an array of tuples [(inserted: Bool, memberAfterInsert: Set.Element)].

Pomerania answered 15/2, 2018 at 18:42 Comment(1)
Nice.. Thank you for your answer but I did that. I want to make it very similar to actual definition of method insert(_:) for single Set element. I want to create a same function but it should accept multple Set elements.Bicycle
Q
0

I think what you are looking for is this:

extension Set {
    
    mutating func insert(_ elements: Element...) {
        insert(elements)
    }
    
    mutating func insert(_ elements: [Element]) {
        for element in elements {
            insert(element)
        }
    }
}

The example in your question is breaking a couple of good software programming principals, such as the single responsibility rule. It seems like your function is trying to both modify the current set and return a new set. Thats really confusing. Why would you ever want to do that?

If you are trying to create a new set from multiple sets, then you can do the following:

extension Set { 

    /// Initializes a set from multiple sets. 
    /// - Parameter sets: An array of sets. 
    init(_ sets: Self...) {
        self = []
        for set in sets {
            for element in set {
                insert(element)
            }
        }
    }
}
Quartersaw answered 14/8, 2020 at 20:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.