Android

Map vs FlatMap

그란. 2021. 4. 1. 15:03

리스트 변환 연산자 map 과 flatMap의 차이와 활용 방법에 대한 정리 

 

(1) map  : iterator()로 각 아이템 꺼내옴 -> 변형 -> 방출 

 

(2) flatMap : iterator() 로 아이템 하나씩 꺼내옴 -> 새로운 리스트로 변형 -> 방출 

 

map은 익히 알고 있는 for문과 동일하기 때문에 이해하기 쉬운데

flatMap은 문서만 보고선 도저히 이해하기 어렵다. ( 개념이 와닿지가 않는다. flat하게 만든다는게 무슨말이지? ) 

 

 

실 예로 flatMap 이해하기  

 

1. 리스트 2배로 변형

fun main() {

    val list = listOf(
        Cat(1, "naong"),
        Cat(2, "persian"),
        Cat(3, "sham")
    )

    //고양이 2배로 증식하기

    val new = list.flatMap{cat->
        listOf(
            Cat(cat.id, cat.name),
            Cat(cat.id, cat.name)
        )
    }
    println(new)
}


data class Cat (
    val id: Int,
    val name: String
)


// [Cat(id=1, name=naong), Cat(id=1, name=naong), Cat(id=2, name=persian),
// Cat(id=2, name=persian), Cat(id=3, name=sham), Cat(id=3, name=sham)]

 

 ( val new = list + list 와는 다른 결과 )

 

 

2. 리뷰 리스트를 조건에 맞게 변형 ( + sealed class )

  • 헤더에는 리뷰추가 버튼이 들어간다 ( 0번 인덱스 )
  • 본문에는 reply 존재유무에 따라 질문, 답변을 구분한다 

이런 형태로 구현할 예정

 

data class Review(
    val id:Int,
    val content:String,
    val reply:String?
)


//위의 리스트를 내가 원하는 ReviewUi의 형태로 변환


sealed class ReviewUi {

	data class Header(
        val id:Int
    ):ReviewUi()
	
    data class Question(
        val id:Int,
        val content:String
    ):ReviewUi()
	
    data class Reply(
        val id:Int,
        val reply:String
    ):ReviewUi()
	
}


fun main() {
 	   

    val list = listOf(
        Review(1,"내용","답변"),
        Review(2,"내용22",null),
        Review(3,"내용33","답변33")
    )

	
    /*  1. 첫번째 항목은 Header가 고정
	2. list의 첫번째 항목과 세번째 항목은 reply가 있으므로 Question, Reply로 나눔
    3. list의 두번째 항목은 reply가 null이므로 Question 만 있음  */


    val new = listOf(
            ReviewUi.Header(0)
           ) + list.flatMap{ review->
            if(review.reply == null){
                    listOf(
                        ReviewUi.Question(review.id, review.content)
                    )
            }else{
                    listOf(
                        ReviewUi.Question(review.id, review.content),
                        ReviewUi.Reply(review.id, review.reply)
                    )
            }
     }
    
     print(new)
     // [Header(id=0), 
     // Question(id=1, content=내용), Reply(id=1, reply=답변), 
     // Question(id=2, content=내용22), 
     // Question(id=3, content=내용33), Reply(id=3, reply=답변33)]
    
}

 

 

 

3. 채팅 리스트를 조건에 맞게 변형 ( + groupBy )

  • 내가 보낸 메시지와 상대방이 보낸 메시지를 구분
  • 날짜가 변경된 경우 각 메시지 사이에 날짜를 넣어줌

이런 화면을 구현

 

data class Chat(
    val id:Int,
    val userName:String,
    val message:String,
    val createdDate:String
)

// 위의 Chat 모델을 아래의 ChatUI형태로 변환

sealed class ChatUi{
    
    data class Me(
        val message:String,
        val date:String
    ) : ChatUi()

    data class You(
        val message:String,
        val date:String
    ) : ChatUi()

    data class Date(
        val date : String
    ) : ChatUi()
}

fun main() {
 	val list = listOf(
        Chat(1, "You", "message1", "2020/04/01"),
        Chat(2, "Me", "message2", "2020/04/01"),
        Chat(3, "Me", "message3", "2020/04/02")
        )
    
    
     val new = list.groupBy{ chat ->
               chat.createdDate
          }.flatMap{ (createdDate,messages)->
               listOf(
                   ChatUi.Date(createdDate),
               ) + 
               messages.map{ message ->
                   if(message.userName == "Me"){
                       listOf(ChatUi.Me(message.message,message.createdDate))
                   }else{
                       listOf(ChatUi.You(message.message,message.createdDate))
                   
                   }
               }
           }
            
          
      print(new)
      
      //[Date(date=2020/04/01), 
      //[You(message=message1, date=2020/04/01)], 
      //[Me(message=message2, date=2020/04/01)], 
      // Date(date=2020/04/02), 
      //[Me(message=message3, date=2020/04/02)]]
        
}

            

 

지금까지 flatMap 의 활용법에 대해 알아보았습니다

 

 

- 추가 : flatMap 좀더 깊게 이해하기

val datas = listOf(
    Student(
        subscribedCourses = listOf(
            Course(name = "Maths", isPaid = false),
            Course(name = "Arts", isPaid = true)
        )
    ),

    Student(
        subscribedCourses = listOf(
            Course(name = "History", isPaid = true),
            Course(name = "Biology", isPaid = true)
        )
    ),
    Student(
        subscribedCourses = listOf(
            Course(name = "Physics", isPaid = true),
            Course(name = "History", isPaid = true)
        )
    )
)

위의 List<Student> 데이터를 flat하게 만들고 싶을때 -> Cource List로 만들기 

datas.flatMap { student ->
    student.subscribedCourses
}

-> [Course(name=Maths, isPaid=false), Course(name=Arts, isPaid=true), 
Course(name=History, isPaid=true), Course(name=Biology, isPaid=true), 
Course(name=Physics, isPaid=true), Course(name=History, isPaid=true)]