user678392
user678392

Reputation: 2031

How do you assign a slice of numbers to an array in swift

x is an object that holds an array called point.

x implements the subscript operator so you can do things, like x[i] to get the array's ith element (of type T, which is usually an Int or Double).

This is what I want to do:

 x[0...2] = [0...2]

But I get an error that says ClosedInterval<T> is not convertible to Int/Double.

Edit1:

Here is my object x:

let x = Point<Double>(dimensions:3)

For kicks and giggles: define x as [1.0,2.0,0.0]

I can get the first n elements via x[0...2].

What I want to know is how to update x[0...2] to hold [0.0, 0.0.0.0] in one fell swoop. Intuitively, I would want to do x[0...2] = [0...2]. This does not work as can be seen in the answers. I want to update x without iteration (on my end) and by hiding the fact that x is not an array (even though it is not).

Upvotes: 4

Views: 2331

Answers (2)

Airspeed Velocity
Airspeed Velocity

Reputation: 40965

[0...2] is an array with one element which, at best, will be a Range<Int> from 0 through 2. You can't assign that to a slice containing, say, Ints.

x[0...2] on the other hand is (probably) a slice, and Sliceable only defines a get subscript, not a setter. So even if the types were more compatible - that is, if you tried x[0...2] = 0...2, which at least is attempting to replace a range within x with the values of a similarly-sized collection - it still wouldn't work.

edit: as @rintaro points out, Array does support a setter subscript for ranges – so if x were a range you could do x[0...2] = Slice(0...2) – but it has to be a slice you assign, so I'd still go with replaceRange.

If what you mean is you want to replace entries 0 through 2 with some values, what you want is replaceRange, as long as your collection conforms to RangeReplaceableCollection (which, for example, Array does):

var x = [0,1,2,3,4,5]
var y = [200,300,400]
x.replaceRange(2..<5, with: y)
// x is now [0,1,200,300,400,5]

Note, the replaced range and y don't have to be the same size, the collection will expand/contract as necessary.

Also, y doesn't have to an array, it can be any kind of collection (has to be a collection though, not a sequence). So the above code could have been written as:

var x = [0,1,2,3,4,5]
var y = lazy(2...4).map { $0 * 100 }
x.replaceRange(2..<5, with: y)

edit: so, per your edit, to in-place zero out an array of any size in one go, you can do:

var x = [1.0,2.0,0.0]
// range to replace is the whole array's range,
// Repeat just generates any given value n times
x.replaceRange(indices(x), with: Repeat(count: x.count, repeatedValue: 0.0))

Adjust the range (and count of replacing entries) accordingly if you want to just zero out a subrange.

Given your example Point class, here is how you could implement this behavior assuming it's backed by an array under the hood:

struct Point<T: FloatLiteralConvertible> {
    private var _vals: [T]

    init(dimensions: Int) {
        _vals = Array(count: dimensions, repeatedValue: 0.0)
    }

    mutating func replaceRange
      <C : CollectionType where C.Generator.Element == T>
      (subRange: Range<Array<T>.Index>, with newElements: C) {

        // just forwarding on the request - you could perhaps
        // do some additional validation first to ensure dimensions
        // aren't being altered...
        _vals.replaceRange(subRange, with: newElements)

    }

}

var x = Point<Double>(dimensions:3)
x.replaceRange(0...2, with: [1.1,2.2,3.3])

Upvotes: 6

Rob Napier
Rob Napier

Reputation: 299345

You need to implement subscript(InvervalType) to handle the case of multiple assignments like this. That isn't done for you automatically.

Upvotes: 0

Related Questions