I have a problem trying to remove a specific object from an array in Swift 3. I want to remove item from an array as in the screenshot but I don't know the solution.
If you have any solutions please share with me.
Short Answer
you can find the index of object in array then remove it with index.
var array = [1, 2, 3, 4, 5, 6, 7]
var itemToRemove = 4
if let index = array.index(of: itemToRemove) {
array.remove(at: index)
}
Long Answer
if your array elements confirm to Hashable protocol you can use
array.index(of: itemToRemove)
because Swift can find the index by checking hashValue of array elements.
but if your elements doesn't confirm to Hashable protocol or you don't want find index base on hashValue then you should tell index method how to find the item. so you use index(where: ) instead which asks you to give a predicate clouser to find right element
// just a struct which doesn't confirm to Hashable
struct Item {
let value: Int
}
// item that needs to be removed from array
let itemToRemove = Item(value: 4)
// finding index using index(where:) method
if let index = array.index(where: { $0.value == itemToRemove.value }) {
// removing item
array.remove(at: index)
}
if you are using index(where:) method in lots of places you can define a predicate function and pass it to index(where:)
// predicate function for items
func itemPredicate(item: Item) -> Bool {
return item.value == itemToRemove.value
}
if let index = array.index(where: itemPredicate) {
array.remove(at: index)
}
for more info please read Apple's developer documents:
index(where:)
index(of:)
According to your code, the improvement could be like this:
if let index = arrPickerData.index(where: { $0.tag == pickerViewTag }) {
arrPickerData.remove(at: index)
//continue do: arrPickerData.append(...)
}
The index existing means Array contains the object with that Tag.
I used the solutions provided here: Remove Specific Array Element, Equal to String - Swift Ask Question
this is one of the solutions there (in case the object was a string):
myArrayOfStrings = ["Hello","Playground","World"]
myArrayOfStrings = myArrayOfStrings.filter{$0 != "Hello"}
print(myArrayOfStrings) // "[Playground, World]"
Related
This question already has answers here:
Get random elements from array in Swift
(6 answers)
Closed 5 years ago.
I want to write an extension which allows me to put in an array and return a set number of elements from that array with no repeated items. How would I do that? This is what I have so far but it is not perfect. It does not consider duplicates and it does not seem like the best way for this to be done. I was thinking it might make sense to use a set for duplicates.
extension Array {
func randomElement(numberOfItems:Int) -> [Element] {
var finalReturn = Array()
for i in 0..<numberOfItems {
finalReturn.append(self[Int(arc4random_uniform(UInt32(self.count)))])
}
return finalReturn
}
}
usage should be like this.
let selected = allData.randomElement(numberOfItems: 10)
Here is one way to do it:
extension Array {
func randomElements(number: Int) -> [Element] {
guard number > 0 else { return [Element]() }
var remaining = self
var chosen = [Element]()
for _ in 0 ..< number {
guard !remaining.isEmpty else { break }
let randomIndex = Int(arc4random_uniform(UInt32(remaining.count)))
chosen.append(remaining[randomIndex])
remaining.remove(at: randomIndex)
}
return chosen
}
}
Sample:
let testArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let randomThree = testArray.randomElements(number: 3)
// randomThree is [1, 5, 4]
Depending on your use case, you may want to change the behavior when the number of elements requested is greater than the number of elements in the array.
In my sample above, if this is the case, I return the maximum number of elements possible (the number of elements in the original array). Alternatively, you could give an error or return nil.
I am trying to make an Array extension in Swift 3.1.1 that supports the addition of an object to a certain index in a 2D Array even if the array hasn't been populated yet. The extension should also provide the ability to get an object at certain indexPath. I have the code for this in Swift 2 but I don't seem to be able to migrate it to Swift 3. This is the Swift 2 code:
extension Array where Element: _ArrayProtocol, Element.Iterator.Element: Any {
mutating func addObject(_ anObject : Element.Iterator.Element, toSubarrayAtIndex idx : Int) {
while self.count <= idx {
let newSubArray = Element()
self.append(newSubArray)
}
var subArray = self[idx]
subArray.append(anObject)
}
func objectAtIndexPath(_ indexPath: IndexPath) -> Any {
let subArray = self[indexPath.section]
return subArray[indexPath.row] as Element.Iterator.Element
}
}
The code is taken from this answer.
As Martin says in his answer here, _ArrayProtocol is no longer public in Swift 3.1, therefore meaning that you cannot use it as a constraint in your extension.
A simple alternative in your case is to instead constrain the Array's Element to being a RangeReplaceableCollection – which both defines an init() requirement meaning "empty collection", and an append(_:) method in order to add elements to the collection.
extension Array where Element : RangeReplaceableCollection {
typealias InnerCollection = Element
typealias InnerElement = InnerCollection.Iterator.Element
mutating func fillingAppend(
_ newElement: InnerElement,
toSubCollectionAtIndex index: Index) {
if index >= count {
append(contentsOf: repeatElement(InnerCollection(), count: index + 1 - count))
}
self[index].append(newElement)
}
}
Note also that we're doing the append as a single call (using append(contentsOf:), ensuring that we only have to resize the outer array at most once.
For your method to get an element from a given IndexPath, you can just constrain the inner element type to being a Collection with an Int Index:
// could also make this an extension on Collection where the outer Index is also an Int.
extension Array where Element : Collection, Element.Index == Int {
subscript(indexPath indexPath: IndexPath) -> Element.Iterator.Element {
return self[indexPath.section][indexPath.row]
}
}
Note that I've made it a subscript rather than a method, as I feel it fits better with Array's API.
You can now simply use these extensions like so:
var arr = [[Int]]()
arr.fillingAppend(6, toSubCollectionAtIndex: 3)
print(arr) // [[], [], [], [6]]
let indexPath = IndexPath(row: 0, section: 3)
print(arr[indexPath: indexPath]) // 6
Although of course if you know the size of the outer array in advance, the fillingAppend(_:toSubCollectionAtIndex:) method is redundant, as you can just create your nested array by saying:
var arr = [[Int]](repeating: [], count: 5)
which will create an [[Int]] array containing 5 empty [Int] elements.
There's no need to limit all these ideas to the concrete Array type.
Here's my solution. This discussion was great in that I just learned about RangeReplaceableCollection. Merging (what I think is) the best of both worlds, I pushed all the operations down (up?) the Type hierarchy as far as possible.
Subscript works on much more than Array as #Hamish says. But also, there's no need to constrain the index type, so we have to get rid of IndexPath. We can always sugar this with typealias Index2d = ...
extension Collection where Self.Element: Collection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
}
}
Why not have a mutable version at the most generic possible level (between Collection and RangeReplaceableCollection) (unfortunately I don't think the getter can be inherited when we redefine subscript):
extension MutableCollection where Self.Element: MutableCollection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
set {
self[indexTuple.row][indexTuple.column] = newValue
}
}
}
Then, if you want to initialize lazily, avoid using init:repeatedValue and revise set to have auto-initialization semantics. You can trap bounds overflow and add missing empty elements in both dimensions by integrating the accepted answer's fillingAppend idea.
And when creating a 2D initializer, why not extend the idea of repeating in the natural way:
extension RangeReplaceableCollection where Element: RangeReplaceableCollection {
init(repeating repeatedVal: Element.Element, extents: (row: Int, column: Int)) {
let repeatingColumn = Element(repeating: repeatedVal, count: extents.column)
self.init(repeating: repeatingColumn, count: extents.row)
}
}
Example Usage:
enum Player {
case first
case second
}
class Model {
let playerGrid: Array<Array<Player>> = {
var p = [[Player]](repeating: .first, extents: (row: 10, column: 10))
p[(3, 4)] = .second
print("Player at 3, 4 is: \(p[(row: 3, column: 4)])")
return p
}()
}
I have written my own small function to find an element in an array using a key. But I'm sure there is a ready to use implementation in Swift to get it in one line. Any hint?
func objectAtKey(array: [T], key: String) -> T? {
for element in array {
if element.name == key {
return element
}
}
return nil
}
I also know function indexOf, but this return an index, I have to use for further access. I think this is slower:
let index = array.indexOf({$0.name == key})
In Swift 3 (Xcode 8, currently beta 6) you can do
if let el = array.first(where: { $0.name == key }) {
// `el` is the first array element satisfying the condition.
// ...
} else {
// No array element satisfies the condition.
}
using the first(where:) method of the Sequence protocol:
/// Returns the first element of the sequence that satisfies the given
/// predicate or nil if no such element is found.
///
/// - Parameter predicate: A closure that takes an element of the
/// sequence as its argument and returns a Boolean value indicating
/// whether the element is a match.
/// - Returns: The first match or `nil` if there was no match.
public func first(where predicate: (Element) throws -> Bool) rethrows -> Element?
I think the best solution for you here is to use the indexOf with a Predicate that you have written. I would have written it like this though:
let array = ["Foo", "Bar", "Test"]
if let i = array.indexOf({$0 == "Foo"}) {
print(array[i])
}
To handle if the value does not exists if you need that.
Try this:
let element = array.filter{ $0.name == key }.first
One nice syntax in Swift is the ability to enumerate both the index and entry of an array as a loop like so:
for (index, entry) in enumerate(myArray) {
doSomething(index, entry)
}
And it's also possible to easily start from the end of the array using the reverse condition like this:
for (index, entry) in enumerate(reverse(myArray)) {
doSomething(index, entry)
}
However, the problem with this is that index now counts from the end of the array (i.e- the last element is index zero), meaning that it has to be corrected if I need an index value for the actual location of entry in myArray, which loses some of the elegance of this.
The alternative is to do something like this:
for (var index, entry) in enumerate(reverse(myArray)) {
index = (myArray.count - 1) - index
doSomething(index, entry)
}
It's not the end of the world, but I'm wondering whether there's an alternative that I've overlooked that could allow me to enumerate in reverse, but with the correct index values being returned?
When you apply reverse() to an array, the result is already a new array, naturally the index should reflect the new array. I found another way to enumerate reversely, I suppose this is elegant enough:
let names = ["Jack", "Lucy", "Kate"]
for index in stride(from: count(names) - 1, through: 0, by: -1) {
println("\(index): \(names[index])")
}
This might be a solution:
let myArray = [1, 2, 3, 4]
for index in reverse(0..<myArray.count) {
println("\(index): \(myArray[index])")
}
Edit:
If you really do not want to access your entry via index inside the for body, you might try:
for (index, entry) in zip(reverse(0..<myArray.count), reverse(myArray)) {
println("\(index): \(entry)")
}
or
for (index, entry) in reverse(0..<myArray.count)
.map({ ($0, myArray[$0])}) {
println("\(index): \(entry)")
}
Hope this helps
Don't think to complicated:
for var i = array.count - 1; i >= 0; i-- {
let item = array[i]
}
I'm looking for a way to sort a Swift array based on a Boolean value.
I've got it working using a cast to NSArray:
var boolSort = NSSortDescriptor(key: "selected", ascending: false)
var array = NSArray(array: results)
return array.sortedArrayUsingDescriptors([boolSort]) as! [VDLProfile]
But I'm looking for the Swift variant, any ideas?
Update
Thanks to Arkku, I've managed to fix this using the following code:
return results.sorted({ (leftProfile, rightProfile) -> Bool in
return leftProfile.selected == true && rightProfile.selected != true
})
Swift's arrays can be sorted in place with sort or to a new array with sorted. The single parameter of either function is a closure taking two elements and returning true if and only if the first is ordered before the second. The shortest way to use the closure's parameters is by referring to them as $0 and $1.
For example (to sort the true booleans first):
// In-place:
array.sort { $0.selected && !$1.selected }
// To a new array:
array.sorted { $0.selected && !$1.selected }
(edit: Updated for Swift 3, 4 and 5, previously sort was sortInPlace and sorted was sort.)
New (for Swift 1.2)
return results.sort { $0.selected && !$1.selected }
Old (for Swift 1.0)
Assuming results is of type [VDLProfile] and VDLProfile has a Bool member selected:
return results.sorted { $0.selected < $1.selected }
See documentation for sorted
Swift’s standard library provides a function called sorted, which sorts an array of values of a known type, based on the output of a sorting closure that you provide
reversed = sorted(array) { $0 > $1 }
reversed will be a new array which will be sorted according to the condition given in the closure.