Rakesha Shastri
Rakesha Shastri

Reputation: 11242

Limit array size in swift

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

Answers (5)

yasirmturk
yasirmturk

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

Cristik
Cristik

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

Dan
Dan

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

Uday Babariya
Uday Babariya

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

timgcarlson
timgcarlson

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

Related Questions