Reputation: 11242
Is there a way to limit array size in swift so that when you append elements to it when it's full, it doesn't append?
I know this can be done programmatically. Wondering if swift has inbuilt handling for this.
eg:
array -> Array with size 10
array.append(1)
.
.
.
array.append(10)
array.append(11) // Doesn't do anything?
array.insert(0, pos: 0)
Use Case: Pushes the last element out of the array to make space for the new element?
Edit - last line is the main use case.
Upvotes: 8
Views: 23576
Reputation: 1954
From swift 5.1 you can use something like this
@propertyWrapper
public struct MaxSizeCollection<Value: Collection> {
private let _maxSize: UInt?
public var _value: Value
public init(wrappedValue value: Value) {
_value = value
_maxSize = nil
}
public init(wrappedValue value: Value, maxSize: UInt) {
_value = value
_maxSize = maxSize
}
public var wrappedValue: Value {
get { _value }
set {
guard let _maxSize = _maxSize, _value.count <= _maxSize else { return }
_value = newValue
}
}
}
and then use it like this
@MaxSizeCollection(maxSize: 128)
var limitSizeArray: [Any] = []
Upvotes: 1
Reputation: 32786
Nope, Swift doesn't come with this kind of array - which is similar to a View from a database - allowing you to peek the first N elements. Though for this both the view and it target should be reference types, which is not the case in Swift for arrays.
But enough blabbing around, you could quickly write a wrapper over an Array
for fulfilling your needs:
/// an array-like struct that has a fixed maximum capacity
/// any element over the maximum allowed size gets discarded
struct LimitedArray<T> {
private(set) var storage: [T] = []
public let maxSize: Int
/// creates an empty array
public init(maxSize: Int) {
self.maxSize = maxSize
}
/// takes the max N elements from the given collection
public init<S: Sequence>(from other: S, maxSize: Int) where S.Element == T {
self.maxSize = maxSize
storage = Array(other.prefix(maxSize))
}
/// adds a new item to the array, does nothing if the array has reached its maximum capacity
/// returns a bool indicated the operation success
@discardableResult public mutating func append(_ item: T) -> Bool {
if storage.count < maxSize {
storage.append(item)
return true
} else {
return false
}
}
/// inserts an item at the specified position. if this would result in
/// the array exceeding its maxSize, the extra element are dropped
public mutating func insert(_ item: T, at index: Int) {
storage.insert(item, at: index)
if storage.count > maxSize {
storage.remove(at: maxSize)
}
}
// add here other methods you might need
}
// let's benefit all the awesome operations like map, flatMap, reduce, filter, etc
extension LimitedArray: MutableCollection {
public var startIndex: Int { return storage.startIndex }
public var endIndex: Int { return storage.endIndex }
public subscript(_ index: Int) -> T {
get { return storage[index] }
set { storage[index] = newValue }
}
public func index(after i: Int) -> Int {
return storage.index(after: i)
}
}
Since the struct conforms to Collection
, you can easily pass it to code that knows only to work with arrays by transforming its contents into an array: Array(myLimitedArray)
.
Upvotes: 7
Reputation: 231
I recently wanted this exact type of collection. I went ahead and created this. It should do exactly what you want with some additional helper functions.
https://gist.github.com/djk12587/ea6d8dea837f8274b911deb0c819f74f
struct CappedCollection<T> {
private var elements: [T]
var maxCount: Int
init(elements: [T], maxCount: Int) {
self.elements = elements
self.maxCount = maxCount
}
}
extension CappedCollection: Collection, ExpressibleByArrayLiteral {
typealias Index = Int
typealias Element = T
init(arrayLiteral elements: Element...) {
self.elements = elements
maxCount = elements.count
}
var startIndex: Index { return elements.startIndex }
var endIndex: Index { return elements.endIndex }
subscript(index: Index) -> Iterator.Element {
get { return elements[index] }
}
func index(after i: Index) -> Index {
return elements.index(after: i)
}
@discardableResult
mutating func append(_ newElement: Element) -> Element? {
elements.append(newElement)
return removeExtraElements().first
}
@discardableResult
mutating func append<C>(contentsOf newElements: C) -> [Element] where C : Collection, CappedCollection.Element == C.Element {
elements.append(contentsOf: newElements)
return removeExtraElements()
}
@discardableResult
mutating func insert(_ newElement: Element, at i: Int) -> Element? {
elements.insert(newElement, at: i)
return removeExtraElements().first
}
@discardableResult
mutating func insert<C>(contentsOf newElements: C, at i: Int) -> [Element] where C : Collection, CappedCollection.Element == C.Element {
elements.insert(contentsOf: newElements, at: i)
return removeExtraElements()
}
private mutating func removeExtraElements() -> [Element] {
guard elements.count > maxCount else { return [] }
var poppedElements: [Element] = []
poppedElements.append(contentsOf: elements[maxCount..<elements.count])
elements.removeLast(elements.count - maxCount)
return poppedElements
}
}
Upvotes: 1
Reputation: 1021
simply count the elements of array before appending new element.
var array :[Int] = [1,2,3,4,5,6,7,8,9,10]
func insertIntoArray(_ value: Int, array: [Int]) -> [Int] {
var arr = array
if arr.count == 10 {
arr.removeLast()
}
arr.append(value)
return arr
}
array = insertIntoArray(11, array: array)
Upvotes: 3
Reputation: 3146
The answer to your question is no. Swift does not have a fixed-size array data structure, nor does it really need to.
If you do want to create a fixed-size array for yourself, Ray Wenderlich has published an interesting tutorial/article on how to do just that in Swift!
Upvotes: 0