87

I am new to Swift. I have been doing Java programming. I have a scenario to code for in Swift.

The following code is in Java. I need to code in Swift for the following scenario

// With String array - strArr1
String strArr1[] = {"Some1","Some2"}

String strArr2[] = {"Somethingelse1","Somethingelse2"}

for( int i=0;i< strArr1.length;i++){
    System.out.println(strArr1[i] + " - "+ strArr2[i]);
}

I have a couple of arrays in swift

var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]

for data in strArr1{
    println(data)
}

for data in strArr2{
    println(data)
}
// I need to loop over in single for loop based on index.

Could you please provide your help on the syntaxes for looping over based on index

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
Lohith
  • 891
  • 1
  • 6
  • 5
  • Your java code is unsafe to begin with, as it assumes that strArr2 is the same length or longer than StrArr1 (which in the hard-coded arrays you have works, but in code where the arrays could change, would not work). – Will M. Mar 23 '15 at 18:40
  • Yes, the java code is just an example. We have a check if strArr1.length == strArr2.length, else we return. – Lohith Mar 23 '15 at 21:58

7 Answers7

166

You can use zip(), which creates a sequence of pairs from the two given sequences:

let strArr1 = ["Some1", "Some2"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]

for (e1, e2) in zip(strArr1, strArr2) {
    print("\(e1) - \(e2)")
}

The sequence enumerates only the "common elements" of the given sequences/arrays. If they have different length then the additional elements of the longer array/sequence are simply ignored.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • So zip() takes 2 arrays, and returns an array of tuples of common elements in both arrays? Is `zip()` swift 2 syntax, or is `zip2`? Your first sentence implies that `zip` is the swift 1.2 syntax, but the last bit makes it sound like `zip` is the Swift 2 syntax. – Duncan C Oct 07 '15 at 20:24
  • @DuncanC: You are right, the last edit was misleading. The `zip()` function works in both Swift 1.2 and Swift 2, only the underlying sequence type changed from `Zip2` to `Zip2Sequence`. – It takes two sequences (arrays are just a special case) and returns a (lazily evaluated) sequence with the tuples of the common elements. – Martin R Oct 07 '15 at 20:38
  • Can we print all elements of two different size array ? – pankaj nigam Aug 31 '19 at 10:03
  • Why do they all have to have the same type? It seems like a pointless limitation Edit: My problem wasn't related to type; gives me the same error anyway: Cannot invoke 'zip' with an argument list of type '([Float], [Float], [Float])' – pete Aug 07 '20 at 19:52
  • 1
    @pete: The Swift `zip` function takes *exactly two* sequences as arguments, Compare https://stackoverflow.com/a/48753610/1187415, or https://forums.swift.org/t/zip3-zip4/218 in the Swift forum. – Martin R Aug 07 '20 at 20:27
36

With Swift 5, you can use one of the 4 following Playground codes in order to solve your problem.


#1. Using zip(_:_:) function

In the simplest case, you can use zip(_:_:) to create a new sequence of pairs (tuple) of the elements of your initial arrays.

let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]

let sequence = zip(strArr1, strArr2)

for (el1, el2) in sequence {
    print("\(el1) - \(el2)")
}

/*
 prints:
 Some1 - Somethingelse1
 Some2 - Somethingelse2
 */

#2. Using Array's makeIterator() method and a while loop

It is also easy to loop over two arrays simultaneously with a simple while loop and iterators:

let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]

var iter1 = strArr1.makeIterator()
var iter2 = strArr2.makeIterator()

while let el1 = iter1.next(), let el2 = iter2.next() {
    print("\(el1) - \(el2)")
}

/*
 prints:
 Some1 - Somethingelse1
 Some2 - Somethingelse2
 */

#3. Using a custom type that conforms to IteratorProtocol

In some circumstances, you may want to create you own type that pairs the elements of your initials arrays. This is possible by making your type conform to IteratorProtocol. Note that by making your type also conform to Sequence protocol, you can use instances of it directly in a for loop:

struct TupleIterator: Sequence, IteratorProtocol {

    private var firstIterator: IndexingIterator<[String]>
    private var secondIterator: IndexingIterator<[String]>

    init(firstArray: [String], secondArray: [String]) {
        self.firstIterator = firstArray.makeIterator()
        self.secondIterator = secondArray.makeIterator()
    }

    mutating func next() -> (String, String)? {
        guard let el1 = firstIterator.next(), let el2 = secondIterator.next() else { return nil }
        return (el1, el2)
    }

}

let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]

let tupleSequence = TupleIterator(firstArray: strArr1, secondArray: strArr2)

for (el1, el2) in tupleSequence {
    print("\(el1) - \(el2)")
}

/*
 prints:
 Some1 - Somethingelse1
 Some2 - Somethingelse2
 */

#4. Using AnyIterator

As an alternative to the previous example, you can use AnyIterator. The following code shows a possible implementation of it inside an Array extension method:

extension Array {

    func pairWithElements(of array: Array) -> AnyIterator<(Element, Element)> {
        var iter1 = self.makeIterator()
        var iter2 = array.makeIterator()

        return AnyIterator({
            guard let el1 = iter1.next(), let el2 = iter2.next() else { return nil }
            return (el1, el2)
        })
    }

}

let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]

let iterator = strArr1.pairWithElements(of: strArr2)

for (el1, el2) in iterator {
    print("\(el1) - \(el2)")
}

/*
 prints:
 Some1 - Somethingelse1
 Some2 - Somethingelse2
 */
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • 1
    love zip! Never knew about that.... I always used counters to iterate through loops... me and my C coding skills haha! – Chris May 28 '21 at 15:20
21

Try This:

zip([0,2,4,6], [1,3,5,7]).forEach {
    print($0,$1)
}

zip([0,2,4,6], [1,3,5,7]).forEach {
    print($0.0,$0.1)
}
Rajesh Kumar
  • 821
  • 12
  • 20
14

You could also enumerate over one array and used the index to look inside the second array:

Swift 1.2:

for (index, element) in enumerate(strArr1) {
    println(element)
    println(strArr2[index])
}

Swift 2:

for (index, element) in strArr1.enumerate() {
    print(element)
    print(strArr2[index])
}

Swift 3:

for (index, element) in strArr1.enumerated() {
    print(element)
    print(strArr2[index])
}
Søren Mortensen
  • 1,663
  • 1
  • 11
  • 25
Jérôme
  • 8,016
  • 4
  • 29
  • 35
  • This has changes to strArr1.enumerate() in the latest version of swift. – robhasacamera Mar 27 '16 at 12:34
  • This code will crash if first array has more elements than second array. You should add `guard strArr2.indices.contains(index) else { break }` at the beginning of the for loop to make it safe. – Imanou Petit Dec 24 '18 at 08:48
0
for(var i = 0; i < strArr1.count ; i++)
{
    println(strArr1[i] + strArr2[i])
}

That should do it. Never used swift before so make sure to test.

Updated to recent Swift syntax

for i in 0..< strArr1.count {
    print(strArr1[i] + strArr2[i])
}
Ron Myschuk
  • 6,011
  • 2
  • 20
  • 32
Erik
  • 1,246
  • 13
  • 31
0

You could use Range if you still want to use for in.

var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]

for i in Range(start: 0, end: strArr1.count) {
    println(strArr1[i] + " - " + strArr2[i])
}
Ian MacDonald
  • 13,472
  • 2
  • 30
  • 51
0
> Incase of unequal count 

let array1 = ["some1","some2"]
let array2 = ["some1","some2","some3"]

var iterated = array1.makeIterator()
let finalArray = array2.map({ 
                 let itemValue = iterated.next()
                 return "\($0)\(itemValue != nil ? "-"+itemValue! : EmptyString)" })

// result : ["some1-some1","some2-some2","some3"]

jeff ayan
  • 819
  • 1
  • 13
  • 16