프로젝트를 하다보면 Collection Type을 순회해야할 때가 있는데, 둘의 차이점을 잘 알지 못한 채 지금까지 사용했었다.
이번 글을 통해 for-in과 forEach과 둘의 차이점에 대해 자세히 알아보겠다.
반복문
for-in과 forEach를 알아보기 전에 반복문에 대해 다시 한번 짚어보자.
반복문은 주어진 조건에 의해 특정 코드 블럭을 반복적으로 실행할 수 있게 해주는 구문이다.
1️⃣ for문
- 횟수에 의한 반복
2️⃣ while문
- 조건에 의한 반복
for-in
for 루프상수 in 순회대상 {
실행구문
}
✅ 설정한 범위만큼 반복이 일어난다.
✅ Collection에 저장되어있는 요소 수만큼 반복이 일어난다.
✅ 루프상수의 이름은 원하는 대로 작성할 수 있다. ex)index, num, k ...
let intArray: [Int] = [1,2,3,4,5]
for num in intArray {
print(num)
}
// 1
// 2
// 3
// 4
// 5
for-in문을 이용하여 intArray 요소 값을 print하는 간단한 예제이다.
첫번째 반복, print(num)에서 num이라는 루프상수에 intArray[0] 대입
두번째 반복, print(num)에서 num이라는 루프상수에 intArray[1] 대입
...
다섯번째 반복, print(num)에서 num이라는 루프상수에 intArray[5] 대입
이런 식으로 배열 요소의 수만큼 반복하기 때문에, for문은 횟수에 의한 반복이라고 할 수 있다.
즉, for-in 반복문을 사용하여 배열의 모든 요소에 루프상수로 접근이 가능하다.
✅ 배열뿐만 아니라 Dictionary, Set 또한 for-in문을 이용하여 접근할 수 있다.
let capitalDic: [String : String] = ["Korea" : "Seoul", "Japan" : "Totyo"]
for (key, value) in capitalDic {
print("\(key)의 수도는 \(value)입니다.")
}
// Japan의 수도는 Totyo입니다.
// Korea의 수도는 Seoul입니다.
for key in capitalDic.keys {
print(key)
}
// Korea
// Japan
for value in capitalDic.values {
print(value)
}
// Totyo
// Japan
📚 Dictionary는 요소들의 순서 없이 Key-Value 쌍으로 이루어진 Collection Type이다.
첫번째 for-in문과 같이 튜플 상수를 각각 선언해서 사용하는 것이 일반적이지만,
key 또는 value값만 반복하고 싶다면 두번째, 세번째 방법처럼 사용하면 된다.
let intSet: Set<Int> = [1,2,3,4,5]
for num in intSet {
print(num)
}
📚 Set은 요소들의 순서와 중복 없이 이루어진 Colletion Type이다.
set은 배열하고 동일하게 for-in문을 사용하면 된다.
다만, 요소들의 순서가 없기 때문에 print 출력 값은 매번 다를 것이다.
forEach
순회대상.forEach {
실행구문
}
✅ 기본적인 원리는 for-in과 동일하다.
✅ 반복 처리 할 작업은 클로저로 작성해서 파라미터로 넘긴다.
✅ Collection에 저장된 요소를 클로저가 반복 실행될 때마다 클로저 상수로 넘겨준다.
let intArray: [Int] = [1,2,3,4,5]
intArray.forEach {
print($0)
}
// 1
// 2
// 3
// 4
// 5
내가 전달한 반복 처리(print)를 intArray 요소의 개수 만큼 반복한다.
intArray.enumerated().forEach {
print("index: \($0), num: \($1)")
}
intArray.indices.forEach {
print("index: \($0), num: \(nums[$0])")
}
index를 알고 싶다면 enumerated 메소드나 indices 메소드를 사용하면 된다.
✅ 배열뿐만 아니라 Dictionary, Set 또한 forEach문을 이용하여 접근할 수 있다.
let capitalDic: [String : String] = ["Korea" : "Seoul", "Japan" : "Totyo"]
capitalDic.forEach {
print("\(key)의 수도는 \(value)입니다.")
}
// Japan의 수도는 Totyo입니다.
// Korea의 수도는 Seoul입니다.
capitalDic.keys.forEach {
print($0)
}
// Korea
// Japan
capitalDic.values.forEach {
print($0)
}
// Totyo
// Japan
let intSet: Set<Int> = [1,2,3,4,5]
intSet.forEach {
print($0)
}
//1
//2
//3
//4
//5
for-in과 forEach의 차이점
1️⃣ continue, break
for-in문은 개발자가 직접 구현하는 "반복문"이다.
forEach는 개발자가 반복하고 싶은 구문을 forEach라는 함수의 인자로 "클로저"로 작성해서 넘겨주는 것이다.
그렇기 때문에, 반복문 안에서만 사용할 수 있는 continue와 break는 for-in에서는 사용 가능하지만,
forEach에서는 사용이 불가능하다.
for num in intArray {
break // ⭕️Succ
continue // ⭕️Succ
}
intArray.forEach {
break // ❌err
continue // ❌err
}
2️⃣ return문
func printForIn() {
var intArray: [Int] = [1,2,3,4,5]
for num in intArray {
print(num)
return
}
}
// 1
for-in문의 경우 Collection을 순회하다가 return을 만나면 반복을 종료함.
func printForEach() {
var intArray: [Int] = [1,2,3,4,5]
intArray.forEach {
print($0)
return
}
}
// 1
// 2
// 3
// 4
// 5
하지만 forEach의 경우 반복문이 아닌 반복하고자 하는 내용을 익명 함수(클로저)로 전달하기 때문에 return을 만난다는 것은 전달한 클로저를 종료하는 것을 뜻한다.
첫번째 반복, print($0) 1을 print ➡️ return
두번째 반복, print($0) 2를 print ➡️ return
...
다섯번째 반복, print($0) 5를 print ➡️ return
위와 같이 첫번째 반복 때는 1을 출력하고 클로저를 return하여 바로 다음 두번째 반복 클로저가 실행된다.
즉, 반복 횟수에 영향을 주지 않는다.
내가 짠 코드
func fetchData(codes: String, onCompleted: @escaping ([ExchangeRateModel]) -> Void) {
repository.getExchangeRateData(codes: codes) { entity in
var model = [ExchangeRateModel]()
entity.forEach {
for (key, value) in countryModelItem {
if $0.currencyCode == key {
let item = ExchangeRateModel(country: value.countryName, currencyName: value.currencyUnit, currencyCode: $0.currencyCode, basePrice: $0.basePrice, changePrice: $0.changePrice, signedChangeRate: $0.signedChangeRate, date: $0.date, time: $0.time)
model.append(item)
break
}
}
}
onCompleted(model)
}
}
현재 개발하고 있는 "바꿔조" 프로젝트의 ViewModel 로직 중 일부이다.
repository의 getExchangeRateData 메소드를 통해 Entity를 fetch하여 ExchangeRateModel로 가공하는 메소드이다.
서버에서 받아 온 원형 데이터(entity)를 순회하며 entity의 currencyCode값과 countryModelItem Dictionary의 키 값과 일치하면 원하는 entity의 값을 추출하여 ExchangeRateModel로 만들어 model에 저장하는 로직이다.
entity와 countryModelItem 두 CollectionType을 모두 순회하며 비교해야하기 때문에 이중 구조로 작성하였다.
'Swift > Swift 기본기' 카테고리의 다른 글
14. 생성자 (Initialization) - 4 (0) | 2023.08.02 |
---|---|
13. 생성자 (Initialization) - 3 (0) | 2023.08.02 |
12. 생성자 (Initialization) - 2 (0) | 2023.08.01 |
11. 생성자 (Initialization) - 1 (0) | 2023.08.01 |
10. 제네릭 (Generic) (0) | 2023.07.31 |