티스토리 뷰

코트카타

 

문자열 내 마음대로 정렬하기

 

문제

 

 

정답

class Solution {
    fun solution(strings: Array<String>, n: Int): List<String> {
        return strings.sortedWith(compareBy({ it[n] }, { it }))
    }
}

'sortedWith(compareBy())' 함수를 사용하면 쉽게 풀 수 있는 문제이다.

 

 

sortedWith(compareBy())

strings.sortedWith(compareBy({n}))

해당 함수는 n의 값을 기준으로 strings(List)의 요소들을 정렬한다.

 

strings.sortedWith(compareBy({ it[n] }, { it }))

compareBy는 다수의 선택자를 인자로 활용할 수 있다.

 

해당 구문에서는 각 문자열의 n번째 문자를 우선적으로 비교하고,

동일한 경우 전체 문자열을 사전순으로 비교하여 정렬하게 된다.

 

 

테스트케이스 조건 충족

 

각 문자열의 n번째 문자로만 strings배열을 정렬하게 되면 2번째 테스트케이스에서

"abce"와 "abcd"의 2번지 값은 "c"로 같은 값을 가지기 때문에 오류가 나게된다.

 


 

질문지 화면 UI

 

효율성 고려

여러 개의 질문지 화면을 만들 때 하나하나 만들 수도 있겠지만

만들어야되는 질문지가 몇십개가 넘어간다면 그것은 상당히 비효율적인 방법이다.

 

본 강의에서는 성능과 효율성을 챙기기 위해

질문 페이지를 하나만 만들어놓고 글자만 교체해서 재사용하는 방식을 채택했다.

 

 해당 방법을 쓰기 위해선 페이지를 넘길 필요가 있는데

그러기 위해서 'viewPager2'라는 라이브러리를 사용할 것이다.

 

 

라이브러리 추가

 

두번째로 위치해있는 build.gradle.kts 더블클릭

 

implementation("androidx.viewpager2:viewpager2:1.0.0")

dependencies 안에 해당구문을 추가한 뒤 

 

 

 

우측 상단에 위치한 'Sync Now'를 눌러주면

해당 앱에서 ViewPager2 라이브러리를 사용할 수 있다.

 

 

QuestionFragment

 

'QuestionFramgent.kt'라는 코틀린 클래스를 하나 만든다.

 

package com.example.androidpractice

import androidx.fragment.app.Fragment

class QuestionFragment : Fragment() {

}

생성한 파일에 Frament를 상속하는 클래스를 선언한다.

 

해당 클래스에 질문지가 들어갈 예정이다.

해당 클래스를 재사용하여 총 4개의 질문지를 만들 것이다.  

 

 

ViewPagerAdapter

// ViewPageAdaptor.kt

package com.example.androidpractice

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

class ViewPagerAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
    override fun getItemCount(): Int {
        return 4
    }

    override fun createFragment(position: Int): Fragment {
        return QuestionFragment.newInstance(position)
    }
}

ViewPagerAdapter라는 클래스를 생성해 FragmentStateAdapter를 상속받는다.

 

질문지의 페이지는 총 4개이므로 getItemCount()의 반환값은 4로 지정한다.

createFragment()는 질문지 클래스의 postion을 인자값으로 받는 newInstance라는 메서드를 반환한다.

 

 

상속받은 클래스의 기본적인 메소드 생성하기

 

클래스명을 우클릭한 다음 Generate 선택

 

 

Implement Methods 선택

 

 

OK버튼을 누르면 클래스가 상속하는 FragmentStateAdapter에서

기본적인 필요한 메소드를 생성할 수 있다.

 

 

TestActivity 레이아웃 구성

<androidx.viewpager2.widget.ViewPager2
	android:id="@+id/viewPager"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	app:layout_constraintBottom_toBottomOf="parent"
	app:layout_constraintEnd_toEndOf="parent"
	app:layout_constraintStart_toStartOf="parent"
	app:layout_constraintTop_toTopOf="parent" />

activity_test.xml에 ViewPager2 위젯을 추가한다.

해당 뷰페이저 안에다 QuestionFragement 클래스를 넣을 것이다.

 

전 방향의 제약조건을 parent로 주게되면 해당 위젯이 화면에 꽉 차게 나오게 된다.

 

 

ViewPager 선언

private lateinit var viewPager: ViewPager2

TestActivity에서 lateinit으로 viewPager 변수를 선언해준다.

viewPager 변수는 onCreate()에서 초기화를 해야 사용할 수 있다.

 

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_test)

    viewPager = findViewById(R.id.viewPager)
    viewPager.adapter = ViewPagerAdapter(this)
    viewPager.isUserInputEnabled = false
}

viewPager변수에 만들어둔 뷰페이저 위젯를 할당해주고,

해당 뷰페이저의 어댑터는 ViewPagerAdapter(this)로 초기화해준다.

(여기서 this는 현재 fragmentActivity에 해당한다.)

 

뷰페이저의 isUserInputEnabled값을 false로 줌으로

사용자가 화면을 좌우로 움직였을 때 새로운 페이지가 나오는 것을 막는다.

질문지의 응답이 정상적으로 선택이 되야 다음으로 넘어가는 로직을 구현할 것이기 떄문이다.

 

 

질문지의 응답 저장하기

class QuestionnaireResults{
    val results = mutableListOf<Int>()

    fun addResponses(response: List<Int>){
        val mostFrequent = response.groupingBy{ it }.eachCount().maxByOrNull { it.value }?.key
        mostFrequent?.let { results.add(it) }
    }
}

질문지의 응답을 저장하는 클래스이다.

해당 클래스에서 저장된 값에 따라 반환될 mbit결과가 달라진다.

 

addResponses의 인자값으로 질문지에 있는 질문 3개의 응답 결과가 Int형 배열로 들어가게 되고

response값에서 요소 중 같은 값이 더 많은 숫자를 results배열에 저장한다.

 

(예를 들어 response에 [1, 1, 2] 라는 값이 들어왔다면 results에 1을 저장한다.)  

 

// response = [1, 1, 2]
val mostFrequent = response.groupingBy{ it }.eachCount().maxByOrNull { it.value }?.key

groupingBy{ it }에서 리스트의 각 요소를 기준으로 그룹화를 진행한다.

(response의 요소는 1 또는 2의 값만이 들어있으므로 숫자 1의 그룹과 2의 그룹으로 나뉘게 된다.)

 

eachCount()에서 그룹화된 결과에 대해 각 그룹에 속한 요소의 수를 세어 맵으로 반환한다.

(이 경우에서 반환값은 {1: 2, 2: 1} 이 됩다.)

 

거기서 maxByOrNull{ it.value }?.key 에서로 최대 값을 가지는

엔트리를 찾아 그 엔트리의 키 값을 반환한다.

(2 > 1 이므로 2의 값을 가지는 키 1이 최종적으로 변수에 저장된다.)

 

mostFrequent?.let { results.add(it) }

'let' 함수는 객체가 null값을 가지지 않을 때, 람다문을 실행시킨다.

mostFrequent가 null값이 아니라면 해당 키값을 results배열에 추가한다.

 

 

페이지 이동 함수

fun moveToNextQuestion(){
    if(viewPager.currentItem==3){
        // 마지막 페이지 -> 결과화면으로 이동
    }else{
        val nextItem = viewPager.currentItem + 1
        if(nextItem < viewPager.adapter?.itemCount?:0){
            viewPager.setCurrentItem(nextItem, true)
        }
    }
}

currentItem은 현재 페이지 번호를 의미하며 0부터 시작한다.

페이지 번호에 따라 마지막 페이지로 이동할지 다음 페이지로 넘어갈지 결정이 된다.

 

nextItem < viewPager.adapter?.itemCount?:0

해당 코드는 ViewPager에서 다음 페이지로 안전하게 이동할 수 있는지를 판단하는 조건을 확인한다.

adapter가 null이 아니라면 adapter의 itemcCount 속성에 접근해 ViewPager의 포함된 총 페이지 수를 반환한다.

 

 

엘비스 연산자

viewPager.adapter?.itemCount?:0

'?:' (엘비스 연산자)는 왼쪽의 표현식이 null일 경우 오른쪽의 값을 반환한다.

해당 코드에서 adpater나 itemCount가 null을 반환한다면 저 구문은 0의 값을 가지게 된다.

 

 

질문 추가

<resources>
    <string name="app_name">myMBTI_Test</string>

    <string name="hello_blank_fragment">Hello blank fragment</string>
    <!-- 질문1 -->
    <string name="question1_title">외향형 - 내향형 (E-I)</string>
    <string name="question1_1">Q1. 데이트가 없는 주말에 나는</string>
    <string name="question1_1_answer1">단톡에 연락해서 친구들과 약속을 잡는다</string>
    <string name="question1_1_answer2">침대랑 하루 종일 물아일체가 된다</string>

    <string name="question1_2">Q2. 친구의 소개로 소개팅에 나온 나는</string>
    <string name="question1_2_answer1">먼저 말 걸면서 분위기를 띄운다</string>
    <string name="question1_2_answer2">말을 걸어올 때까지 기다리고 본다</string>

    <string name="question1_3">Q3. 데이트 중 길에서 연인의 친구를 만난다면 나는</string>
    <string name="question1_3_answer1">자연스럽게 웃으며 대한다</string>
    <string name="question1_3_answer2">무생물이 되어 조용히 있는다</string>

    <!-- 질문2 -->
    <string name="question2_title">감각형 - 직관형 (N-S)</string>
    <string name="question2_1">Q1. 데이트 중 맛있어 보이는 밥집을 발견한 나는</string>
    <string name="question2_1_answer1">간판에서 맛집의 기운이 느껴진다 맛집 각이야</string>
    <string name="question2_1_answer2">유명하고 리뷰도 많으니까 맛은 보장되어 있겠군</string>

    <string name="question2_2">Q2. 오늘 본 영화를 궁금해하는 연인에게 나는</string>
    <string name="question2_2_answer1">좀비랑 싸우는데 주인공이 완전 멋져 보는 내내 소름 돋았어</string>
    <string name="question2_2_answer2">주인공이 좀비 바이러스가 퍼져서 치료하기 위한 영화야</string>

    <string name="question2_3">Q3. 연인에게 줄 선물을 고르게 된 나는</string>
    <string name="question2_3_answer1">실용성은 없어도 예쁘고 기억에 남을 선물</string>
    <string name="question2_3_answer2">연인에게 요즘 가장 필요할 것 같은 선물</string>

    <!-- 질문3 -->
    <string name="question3_title">사고형 - 감정형 (T-F)</string>
    <string name="question3_1">Q1. 연인과 사소한 일로 다퉜을 때 나는</string>
    <string name="question3_1_answer1">ㅇㅇ점은 꼭 고쳐줬으면 좋겠어 이렇게 하면 되잖아</string>
    <string name="question3_1_answer2">나!! 진짜!! 너무!!! 화났어!!!!!</string>

    <string name="question3_2">Q2. 축 처진 연인이 우울하다고 말했을 때 나는</string>
    <string name="question3_2_answer1">왜 우울해? 뭐 때문에 우울한 거야?</string>
    <string name="question3_2_answer2">5초 만에 감정이입 완료. 같이 글썽거린다</string>

    <string name="question3_3">Q3. 힘들게 이벤트를 준비한 나를 신나게 할 연인의 칭찬은?</string>
    <string name="question3_3_answer1">고마워 요즘 바쁠 텐데 언제 이런 걸 생각했어?</string>
    <string name="question3_3_answer2">사랑해 최고야 나 완전 감동했어</string>

    <!-- 질문4 -->
    <string name="question4_title">판단형 - 인식형 (J-P)</string>
    <string name="question4_1">Q1. 데이트 룩을 고를 때 나는</string>
    <string name="question4_1_answer1">전날부터 머리부터 발끝까지 세팅해 준다</string>
    <string name="question4_1_answer2">나가기 직전 마음에 드는 옷을 입는다</string>

    <string name="question4_2">Q2. 썸 중에 연인이 집에 놀러 온다고 했을 때 나는</string>
    <string name="question4_2_answer1">쓰레기 버리기부터 화장실 청소까지 한다</string>
    <string name="question4_2_answer2">보이는 곳만 일단 급하게 치워둔다</string>

    <string name="question4_3">Q3. 커플 해외여행 계획을 짜게 된 나는</string>
    <string name="question4_3_answer1">할 거면 제대로! 일별로 세부 일정을 정리한다</string>
    <string name="question4_3_answer2">비행기 표만 끊어두고 계획의 80% 끝난다고 생각한다</string>

</resources>

string.xml에 해당 리소스를 추가한다.

여기서 질문들을 작성하고 name값을 통해 원하는 질문을 빼올 것이다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함