Get index of an array and update it

I didn't think I'd struggle with this but I'm completely lost here.

I have two arrays and want to update one of the arrays with data from the other and all I need to do is grab the array items one by one by the index and I'm getting the most confusing errors I've ever seen.

Sorry, I'm just so frustrated with Swift.

let range = 1...8

for number in range {
  // code
}

I've tried array1.index(of: number), array1.firstIndex(number) amongst other things as well but the code is just not compiling.

Array 1 contains the same structure of data as array 2, of course the actual data itself held is different and I want to just loop through each and get the first set of data in the first array and compare it with the equivalent in array 2.

In php all I'd do is

for ($i=0; $i<9; $i++) {
  var_dump($array1[$i])
  var_dump($array2[$i])
}

I want to basically do that... but in Swift...

Thanks

Without the error I can't provide any help there, but Array has a subscript you can use for this.

for i in 1...8 {
  print(array1[i])
  print(array2[i])
}

However, Swift arrays require the indices to exist, so if you don't yet have anything at those indices the program will crash, as you're out of bounds. So you often shouldn't use indices at all. Instead, you can use a higher order construct. If the arrays are the same size, zip is easy.

for (first, second) in zip(array1, array2) {
  print(first)
  print(second)
}
1 Like
for number in range {
  print(number)
  print(myArray[number])
}

So that above code gives me 1 in the console and then I get an error that stops code execution due to...

Index out of range
...crashed due to an out of range index.

The structure of my arrays is the following...

[(id:Int, name:String)]

Yes, that's the issue I was taking about: if your array doesn't have a second item, array[1] will crash. Indices are usually a last resort in Swift.

1 Like

You've got to clamp the indices for this kind of operation. Unfortunately, this overload is missing from the standard library:

public extension ClosedRange where Bound: Strideable, Bound.Stride: SignedInteger {
  @inlinable func clamped(to limits: Range<Bound>) -> Self {
    clamped(to: .init(limits))
  }
}
#expect((1...8).clamped(to: [0, 1, 2].indices) == 1...2)
1 Like

Okay, since nobody did that explicitly so far, I'll go on and literally translate that (with some more output to clear things:

for index in 0...8 {
    if index < array1.count {
        print("array1 at index \(index): \(array1[index])")
    }
    if index < array2.count {
        print("array2 at index \(index): \(array2[index])")
    }
}

The errors you see are because you are trying to access an element that does not exist, i.e. the index you pass to the subscript (that's the [ ]) is greater (or equal) to the number of elements in the array. This is not allowed in Swift (and many other languages either) and traps your program's execution. This is different in PHP (where arrays aren't even actually arrays...). AFAIK it gives a warning, but simply returns null.

You will find that Swift is in general stricter with these things (which is a good thing, believe me...).

More generally speaking, it might help us to understand what exactly you're trying to achieve. For example, I wonder where you get that range and what these arrays actually contain. If you have one array and simply want to see whether each element is contained in the other array, you could e.g. use this[1]:

for element in array1 {
    if array2.contains(element) {
        print("element \(element) is also in array2!")
    }
}

Note that for large arrays this is inefficient (because contains has to constantly loop over array2 to check each element), so for large arrays it's better to use a dictionary or a set, like this[2]:

let array2Set = Set(array2)
for element in array1 {
    if array2Set.contains(element) {
        print("element \(element) is also in array2!")
    }
}

There's a bunch of other things that could be said here, but as I don't know what your goal is, I'll leave not at that for now.
May I ask, am I correct in assuming you're in the process of learning Swift and don't have much experience with strongly typed and/or compiled languages yet? I deduce that from the PHP example, but please correct me if I am wrong. I'd be glad to offer further help if you want. :smiley:


  1. I just realized your array contains tuples, in which case there are some ramifications, but the next snippet should still work ↩︎

  2. But in your case that won't work out of the box with tuples as elements since they're not Hashable, but this goes beyond the example for now, I guess ↩︎

3 Likes

I guess something like zip(a, b).enumerated() should be enough to iterate over two arrays with need to access by index, and don’t worry about index bounds. It’s not obvious for the language newcomer though.

Alternatively, more simple and obvious solution (don’t know specifics of PHP access, but in majority of languages that’s basic need): let upperBound = min(a.count, b.count) and then iterate to this boundary in a simple for-loop.