상상하라 그리고 현실로 만들어라.

상상하는 모든 것이 미래다.

Kotlin과 Android/Kotlin

코틀린 컬렉션에 활용할 수 있는 filter, map, all, any 등 함수 API 알아보기

월터제이(Walter J) 2021. 2. 10. 09:00

안녕하세요, 반갑습니다!

 

코틀린으로 안드로이드 앱을 개발하다보면, 자바와 비슷한 것이 많아 편함을 느낍니다.

익숙하죠. 자바만 알고 있다면 유추해서 코틀린으로도 해당 기능을 구현할 수 있으니까요.

그래도 코틀린은 역시 다른 언어임을 느낄 수 있습니다.

 

람다식 및 컬렉션에 대해 자바보다 훨씬 간단하고 효율적으로 구현할 수 있거든요.

오늘은 그 중 컬렉션에 활용할 수 있는 filter, map, all, any 등 함수형 API에 대해 정리해봤습니다.

 

 

코틀린 함수형 API

filter, map, all, any,count, find, maxByOrNull, groupBy

 

 

 

먼저 모든 함수를 사용해보기 전에, 데이터 클래스를 하나 정의해놓도록 하겠습니다.

//데이터 저장용도로만 사용하는 클래스 Movies 정의
data class Movies(
    var title:String,
    var rating:Float) {
}

단순하게 영화 제목과 별점의 정보를 가지는 클래스를 정의했습니다.

정보만을 저장하기 위해 사용하는 용도로 만드는 클래스는 data 키워드를 붙일 수 있습니다.

 

그리고 아래와 같이 데이터를 넣었습니다.

val movieInfo = listOf<Movies>(
    Movies("기생충", 9.07f),
    Movies("세자매", 8.83f),
    Movies("스푸트닉", 4.78f),
    Movies("내겐 너무 어려운 연애", 7.75f),
    Movies("페어웰", 7.91f),
    Movies("어니스트 씨프", 9.55f)
)

이제 이 클래스를 기반으로 모든 함수를 사용해보고, 어떤차이가 있는지 살펴보겠습니다.

 

필수형 함수

필수형 함수라고 굳이 구분한 이유는 대부분의 컬렉션에 대한 연산을 이 함수들로 처리할 수 있기 때문입니다.

함수는 filter() 와 map() 입니다.

 

1. filter()

var movie = movieInfo.filter { it.rating > 8.0f }
movie.forEach {
    println("평점 8점 이상의 영화 : ${it}")
}

8.0 이상의 평점을 가진 영화가 어떤 것들인지 찾는 로직입니다.

 

 

결과를 보면, 정확하게 8.0이상의 영화들이 모아진 것을 확인할 수 있습니다.

아주 간단하죠?

 

2. map()

var movie = movieInfo.filter { it.rating > 8.0f }.map { it.title }
movie.forEach {
    println(it)
}

역시 평점 8.0 이상의 영화를 조회하는 로직입니다.

흠 filter 와 별다를 게 없는 것 같네요.

하지만 일단 결과를 보면 그 차이를 확실히 알 수 있습니다.

 

 

 

어떤가요? filter() 와 map() 의 차이가 구분되시나요?

 

간단히 요약하면, filter() 는 컬렉션을 true를 반환하는 원소만 모읍니다.

그래서 결과는 주어진 술어를 만족하는 원소만으로 이루어진 새로운 컬렉션이 되는 것이죠.

하지만 원소를 변형할 수는 없습니다.

그러기 위해 사용하는 것이 바로 map() 입니다.

.map { it.tile } 은 술어를 만족하는 요소들 중에서 제목만을 따로 뽑아 새로운 컬렉션을 만듭니다.

이렇게 하면 영화 이름만 모인 리스트로 바꿀 수 있습니다.

 

그 외 유용한 함수

이제 정리해볼 함수들은 코드를 더 이해하기 쉽고 깔끔하게 작성하는데 도움이 되는 함수들입니다.

사용해보면서 정말 유용하다는 걸 느낄 수 있었습니다.

 

1. all()

val isRatingOver8 = { m:Movies -> m.rating > 8.0f}		//술어
println("모두 평점 8점 이상의 영화들인가요? : ${movieInfo.all(isRatingOver8)}")

평점 8점 이상의 영화들이 있는지 확인하는 로직입니다.

 

 

 

결과는 true / false 입니다.

딱 모든 원소가 술어를 만족하는지 그 여부만 알 수 있습니다.

all() 은 모든 원소가 술어를 만족하는지 확인하는 함수입니다.

 

2.any()

val isRatingUnder8 = { m:Movies -> m.rating < 8.0f}	//술어
println("평점 8점 미만의 영화가 있나요? : ${movieInfo.any(isRatingUnder8)}")

평점 8 점 미만의 영화가 있는지 확인하는 로직입니다.

 

all() 과 같이 결과값은 true / false 입니다.

술어를 만족하는 원소가 1개라도 있는지 궁금할때는 any() 를 사용하면 됩니다.

 

3.count()

val isRatingOver8 = { m:Movies -> m.rating > 8.0f}		//술어
println("평점 8점 이상의 영화는 몇 편이 있나요? : ${movieInfo.count(isRatingOver8)}")

평점 8점 이상의 영화가 몇 편인지 확인하는 로직입니다.

 

 

결과에서 보듯이 술어를 만족하는 값이 몇개인지 확인할 수 있습니다.

그런데 보통 컬렉션의 크기를 구할때 size() 가 제일 먼저 떠오르곤 합니다.

하지만 size() 와 비교하여 count() 를 쓰는 것이 조금 더 효율적입니다.

 

4.find()

val firtUnder8 = movieInfo.find { it.rating < 8.0f }
println("평점 8점이 안되는 첫 번째 영화는 ${firtUnder8}")

평점 8점이 안되는 영화 중 가장 첫 번째에 있는 영화를 조회하는 로직입니다.

 

입력한 데이터와 결과를 보면, 평점 8점이 안되는 영화 중 가장 처음에 있는 값이 반환됨을 알 수 있습니다.

 

6.groupBy()

groupBy() 는 특정 값을 기준으로 여러 그룹으로 나누는 함수이다.

따라서 기존 데이터 클래스에 기준이 되는 멤버변수를 만들었습니다.

data class MoviesForGroup(
    var title:String,
    var rating:Float,
    var isDomestic:Boolean)		//추가, 국내영화인지, 해외영화인지

그리고 데이터를 추가해줬습니다.

val movieInfo = listOf<MoviesForGroup>(
    MoviesForGroup("기생충", 9.07f, true),
    MoviesForGroup("세자매", 8.83f, true),
    MoviesForGroup("스푸트닉", 4.78f, false),
    MoviesForGroup("내겐 너무 어려운 연애", 7.75f, false),
    MoviesForGroup("페어웰", 7.91f, false),
    MoviesForGroup("어니스트 씨프", 9.55f, false)
)

 

 

이제 그룹지어 볼까요?

val movieGroup = movieInfo.groupBy { it.isDomestic }
movieGroup.forEach {
    println("${it}")
}

isDomestic 값을 기준으로 그룹이 나누어 집니다.

 

isDomestic 값을 기준으로 그룹지어지는 것을 볼 수 있습니다.

GroupBy() 는 key와 value 로 이루어지는 Map 으로 결과를 반환합니다.

그럼 key 는 isDomestic 이 되고, value 가 요소가 되겠지요?

 

 

 

확실히 이렇게 컬렉션에 대한 코틀린 함수들을 정리해보니 자바에서 많이 개선되었음을 느낄 수 있습니다.

감사합니다!

반응형