Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Add remove(where:) for Set and Dictionary 🎯 #2647

Closed
wants to merge 2 commits into from

Conversation

krishpranav
Copy link

This PR introduces a powerful and highly-requested feature: the remove(where:) method for both Set and Dictionary. This enhancement allows developers to efficiently remove elements from these collections based on a condition, leveraging Swift's expressive closures.

Motivation

🔍 Current implementations make it challenging to remove elements efficiently from Set and Dictionary based on conditions. Developers either need to iterate over snapshots (causing unnecessary copying) or collect items in a separate collection for removal. This PR addresses these inefficiencies by providing a native, performant solution.

What's New?

1️⃣ Set.remove(where:): Removes the first element satisfying the condition, if any.
2️⃣ Dictionary.remove(where:): Removes all key-value pairs satisfying the condition.
3️⃣ Comprehensive unit tests included to ensure robustness.

Example Usage

For Set:

var numbers: Set = [1, 2, 3, 4, 5]

// Remove the first even number
if let removedElement = numbers.remove(where: { $0 % 2 == 0 }) {
    print("Removed element: \(removedElement)")  // Output: Removed element: 2
}

print(numbers)  // Output: [1, 3, 4, 5]

For Dictionary:

var people = ["Alice": 25, "Bob": 30, "Charlie": 35]

// Remove people younger than 30
people.remove(where: { $0.value < 30 })

print(people)  // Output: ["Bob": 30, "Charlie": 35]

Implementation Details

  • Introduced remove(where:) as mutating functions in Set and Dictionary.
  • Optimized for performance, directly modifying the underlying storage.
  • Added extensive tests in test/stdlib/Set.swift and test/stdlib/Dictionary.swift.

Why This Matters

💡 This feature simplifies common use cases, improving Swift's ergonomics and efficiency for developers. It's an essential step toward making Swift's standard library more versatile and user-friendly.

References

@@ -0,0 +1,148 @@
# Add `remove(where:)` Method for `Set` and `Dictionary`

* **Proposal**: [SE-0456](0456-remove-where-for-set-and-dictionary.md)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proposal numbers are assigned by the review manager when review starts. The text (and filename) should be 0000- or NNNN- for now to avoid confusion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed it.

@stephentyrone
Copy link
Contributor

The proposal seems mostly OK, but I have some high-level questions about this proposed API. First off, it really should be something like removeFirst(where:)--remove(where:) makes me think that it's going to remove every element that satisfies the predicate (i.e. the inverse of filter). But that's also kind of weird, because Sets and Dictionaries do not have a semantically meaningful notion of "first"--there's an ordering, but it's more of a necessary inconvenience than a meaningful part of the API (and it's unstable run to run).

Can you give some examples of the real problems that you want to use this API to solve? I have some ideas about how I would use it, but I'd like to see some others and think about what other shapes this API might take that aren't as fundamentally dependent on the ordering of Set or Dictionary elements.

@krishpranav
Copy link
Author

@stephentyrone kindly give me sometime, will get back to you.

numbers.remove(element)
break
}
}
Copy link
Contributor

@stephentyrone stephentyrone Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For anyone following along, I would write this:

removedElement = numbers.first(where: { $0.isMultiple(of: 2) }).map {
  numbers.remove($0)!
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants