https://school.programmers.co.kr/learn/courses/30/lessons/12973

 

문제 설명

짝지어 제거하기는, 알파벳 소문자로 이루어진 문자열을 가지고 시작합니다. 먼저 문자열에서 같은 알파벳이 2개 붙어 있는 짝을 찾습니다. 그다음, 그 둘을 제거한 뒤, 앞뒤로 문자열을 이어 붙입니다. 이 과정을 반복해서 문자열을 모두 제거한다면 짝지어 제거하기가 종료됩니다. 문자열 S가 주어졌을 때, 짝지어 제거하기를 성공적으로 수행할 수 있는지 반환하는 함수를 완성해 주세요. 성공적으로 수행할 수 있으면 1을, 아닐 경우 0을 리턴해주면 됩니다.

예를 들어, 문자열 S = baabaa 라면
b aa baa → bb aa → aa →
의 순서로 문자열을 모두 제거할 수 있으므로 1을 반환합니다.

 

제한사항

  • 문자열의 길이 : 1,000,000이하의 자연수
  • 문자열은 모두 소문자로 이루어져 있습니다.

 

배열의 첫번째 값부터 계속 반복하면서 조건문을 만족하는지 확인하기 위해 for문의 i를 -1로 재할당 했지만, 시간 초과로 효율성 테스트를 통과하지 못한다.

// 정확성 테스트 코드 다 통과, 효율성 테스트 다 실패
function solution(s) {
    let s_arr = [...s]

    for(let i = 0; i < (s_arr.length-1); i++) {
        if(s_arr[i] === s_arr[i+1]) {
            s_arr.splice(i, 2)
            i = -1
        }
    }
    
    return s_arr.length === 0 ? 1 : 0
}

스택(선입후출, First In Last Out) 구조를 생각해 빈 배열을 만든 후, 하나씩 값을 push해 비교 후 제거한다.

정확성 테스트, 효율성 테스트 모두 통과

function solution(s) {
    let stack = []
    
    for (let i = 0; i < s.length; i++) {
        stack.push(s[i])
        
        if (stack[stack.length - 1] === stack[stack.length - 2]) {
            stack.pop()
            stack.pop()
        }
    }
    
    return stack.length === 0 ? 1 : 0
}

 

https://school.programmers.co.kr/learn/courses/30/lessons/12909

 

문제 설명

괄호가 바르게 짝지어졌다는 것은 '(' 문자로 열렸으면 반드시 짝지어서 ')' 문자로 닫혀야 한다는 뜻입니다. 예를 들어

  • "()()" 또는 "(())()" 는 올바른 괄호입니다.
  • ")()(" 또는 "(()(" 는 올바르지 않은 괄호입니다.

'(' 또는 ')' 로만 이루어진 문자열 s가 주어졌을 때, 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요.

 

제한사항

  • 문자열 s의 길이 : 100,000 이하의 자연수
  • 문자열 s는 '(' 또는 ')' 로만 이루어져 있습니다.

 

스택(선입후출, First In Last Out) 구조를 생각해 빈 배열을 만든 후, 하나씩 값을 push해 비교 후 제거한다.

function solution(s){
    let stack = []
    
    for(let i = 0; i < s.length; i++) {
        stack.push(s[i])
        
        if(stack[stack.length - 1] === ')' && stack[stack.length - 2] === '(') {
            stack.pop()
            stack.pop()
        }
    }
    
    
    return stack.length === 0 ? true : false
}

https://school.programmers.co.kr/learn/courses/30/lessons/12941

 

문제 설명

길이가 같은 배열 A, B 두개가 있습니다. 각 배열은 자연수로 이루어져 있습니다.
배열 A, B에서 각각 한 개의 숫자를 뽑아 두 수를 곱합니다. 이러한 과정을 배열의 길이만큼 반복하며, 두 수를 곱한 값을 누적하여 더합니다. 이때 최종적으로 누적된 값이 최소가 되도록 만드는 것이 목표입니다. (단, 각 배열에서 k번째 숫자를 뽑았다면 다음에 k번째 숫자는 다시 뽑을 수 없습니다.)

예를 들어 A = [1, 4, 2] , B = [5, 4, 4] 라면

  • A에서 첫번째 숫자인 1, B에서 첫번째 숫자인 5를 뽑아 곱하여 더합니다. (누적된 값 : 0 + 5(1x5) = 5)
  • A에서 두번째 숫자인 4, B에서 세번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 5 + 16(4x4) = 21)
  • A에서 세번째 숫자인 2, B에서 두번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 21 + 8(2x4) = 29)

 

즉, 이 경우가 최소가 되므로 29를 return 합니다.
배열 A, B가 주어질 때 최종적으로 누적된 최솟값을 return 하는 solution 함수를 완성해 주세요.

 

제한사항

  • 배열 A, B의 크기 : 1,000 이하의 자연수
  • 배열 A, B의 원소의 크기 : 1,000 이하의 자연수

 

최솟값을 만들려면 가장 작은 숫자와 가장 큰 숫자를 곱하면 되기 때문에 내림차순, 오름차순으로 정렬한다.

function solution(A,B){
    const a_arr = A.sort((a, b) => a - b)
    const b_arr = B.sort((a, b) => b - a)
    let sum = 0
    
    for(let i = 0; i < a_arr.length; i++) {
        sum = sum + a_arr[i] * b_arr[i]
    }
    
    return sum
}

버전에 맞춰서 강의와 다른 코드가 있습니다.

 

결과 화면


파일 구조

  • src/components/ToDo.js

state의 id값을 이용해서 상세페이지로 보내준다.

import React from 'react'
import { Link } from 'react-router-dom'
import { useDispatch } from 'react-redux'
import { deleteToDo } from '../store/store'

function ToDo(props) {
  // ** Hooks
  const dispatch = useDispatch()

  function onDelete(e) {
    e.preventDefault()
    dispatch(deleteToDo(props.id))
  }

  return (
    <li>
      <Link to={`/${props.id}`}>
        <p>{ props.text }</p>
      </Link>
      <button onClick={onDelete}>DEL</button>
    </li>
  )
}

export default ToDo

 

  • src/routes/Detail.js

useParams 훅을 사용해서 url의 파라미터값을 가지고 온다.

useSelector를 사용해 state를 가지고 온다.

state의 id값과 파라미터의 id값을 비교해 해당 리스트를 가지고 온다. (파라미터의 타입이 String이기 때문에 Number로 타입을 변환해 값을 비교한다.)

import React from 'react'
import { useParams } from 'react-router-dom'
import { useSelector } from 'react-redux'

function Detail() {
  // ** Hooks
  const param = useParams()

  // ** Redux States
  const toDos = useSelector(state => state)

  const toDo = toDos.find(list => list.id === Number(param.id))

  return (
    <>
      <h1>Detail</h1>

      <p>Created: {toDo.id}</p>
      <p>{toDo.text}</p>
    </>
  )
}

export default Detail

버전에 맞춰서 강의와 다른 코드가 있습니다.

 

결과 화면


파일 구조

  • src/components/ToDo.js

To Do List 추가하기 코드에서 dispatch, onDelete() 함수와 클릭 이벤트를 추가했다.

import React from 'react'
import { useDispatch } from 'react-redux'
import { deleteToDo } from '../store/store'

function ToDo(props) {
  // ** Hooks
  const dispatch = useDispatch()

  function onDelete(e) {
    e.preventDefault()
    dispatch(deleteToDo(props.id))
  }

  return (
    <li>
      { props.text } <button onClick={onDelete}>DEL</button>
    </li>
  )
}

export default ToDo

 

  • src/routes/Home.js

To Do List 추가하기 코드에서 ToDo 컴포넌트에 id 값이 추가됐다.

import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { addToDo } from '../store/store'
import ToDo from '../components/ToDo'

function Home() {
  // ** Hooks
  const dispatch = useDispatch()

  // ** States
  const [text, setText] = useState('')

  // ** Redux States
  const toDos = useSelector(state => state)

  function onChange(e) {
    setText(e.target.value)
  }

  function onSubmit(e) {
    e.preventDefault()
    dispatch(addToDo(text))
    setText('')
  }

  return (
    <>
      <h1>To Do</h1>

      <form onSubmit={onSubmit}>
        <input type="text" value={text} onChange={onChange}/>
        <button>Add</button>
      </form>

      <ul>
        {
          toDos.map((toDo, index) => {
            return (
              <ToDo id={toDo.id} text={toDo.text} key={index}/>
            )
          })
        }
      </ul>
    </>
  )
}

export default Home

버전에 맞춰서 강의와 다른 코드가 있습니다.

 

결과 화면


파일 구조

  • src/index.js

Provider는 <App/> 컴포넌트가 리덕스의 store에 접근할 수 있도록 한다.

import React from 'react'
import ReactDOM from 'react-dom/client'
import {Provider} from 'react-redux'
import store from './store/store'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Provider store={store}>
    <App/>
  </Provider>
)

 

  • src/components/ToDo.js

<ToDo/> 컴포넌트를 만들어 props로 데이터를 전달한다.

import React from 'react'

function ToDo(props) {
  return (
    <li>
      { props.text }
    </li>
  )
}

export default ToDo

 

  • src/routes/Home.js

useSelector를 사용해 store의 state를 가지고 온다.

import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { addToDo } from '../store/store'
import ToDo from '../components/ToDo'

function Home() {
  // ** Hooks
  const dispatch = useDispatch()

  // ** States
  const [text, setText] = useState('')

  // ** Redux States
  const toDos = useSelector(state => state)

  function onChange(e) {
    setText(e.target.value)
  }

  function onSubmit(e) {
    e.preventDefault()
    dispatch(addToDo(text))
    setText('')
  }

  return (
    <>
      <h1>To Do</h1>

      <form onSubmit={onSubmit}>
        <input type="text" value={text} onChange={onChange}/>
        <button>Add</button>
      </form>

      <ul>
        {
            toDos.map((toDo, index) => {
              return (
                <ToDo text={toDo.text} key={index}/>
              )
            })
         }
      </ul>
    </>
  )
}

export default Home

결과 화면


버전

react: ^18.2.0

react-dom: ^18.2.0

react-redux: ^8.0.5

react-router-dom: ^6.10.0

react-scripts: 5.0.1

redux: ^4.2.1

 

위의 버전에 맞춰서 강의와 다른 코드가 있습니다.

 

파일 구조

  • src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <App/>
)

 

  • src/App.js
import React from 'react'
import {BrowserRouter, Routes, Route} from 'react-router-dom'
import Home from './routes/Home'
import Detail from './routes/Detail'

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home/>}></Route>
        <Route path="/:id" element={<Detail/>}></Route>
      </Routes>
    </BrowserRouter>
  )
}

export default App

 

  • src/store/store.js
import { createStore } from 'redux'

const ADD = 'ADD'
const DELETE = 'DELETE'

export const addToDo = text => {
  return {
    type: ADD,
    text
  }
}

export const deleteToDo = id => {
  return {
    type: DELETE,
    id
  }
}

const reducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [{ id: Date.now(), text: action.text}, ...state]
    case DELETE:
      return state.filter(toDo => toDo.id !== action.id)
    default:
      return state
  }
}

const store = createStore(reducer)

export default store

 

  • src/routes/Home.js
import React, { useState } from 'react'

function Home() {
  const [text, setText] = useState('')

  function onChange(e) {
    setText(e.target.value)
  }

  function onSubmit(e) {
    e.preventDefault()
    setText('')
    console.log(text)
  }

  return (
    <>
      <h1>To Do</h1>

      <form onSubmit={onSubmit}>
        <input type="text" value={text} onChange={onChange}/>
        <button>Add</button>
      </form>

      <ul></ul>
    </>
  )
}

export default Home

 

  • src/routes/Detai.js
export default () => 'Detail'

결과 화면


1. 바닐라 자바스크립트로 투두 리스트 만들기

  • index.html
<body>
    <h1>To dos</h1>

    <form>
        <input type="text">
        <button>Confirm</button>
    </form>

    <ul></ul>
</body>

 

  • index.js
// html 태그 선택
const form = document.querySelector('form')
const input = document.querySelector('input')
const ul = document.querySelector('ul')

// li에 input의 value 값을 넣어 화면에 보여준다.
const createToDo = toDo => {
  const li = document.createElement('li')
  li.innerText = toDo
  ul.appendChild(li)
}

// 폼을 제출하면 input을 비우고, createToDo 함수를 실행한다.
const onSubmit = e => {
  e.preventDefault()
  const toDo = input.value
  input.value = ''
  createToDo(toDo)
}

form.addEventListener('submit', onSubmit)

 

문제점

데이터를 저장하는 것이 아니라 단순히 innerText를 통해 input.value의 내용을 화면에 보여주는 것이다.

새로고침 시, 데이터가 날아간다.

 


2. 리덕스로 투두 리스트 만들기

  • index.html
<body>
    <h1>To dos</h1>

    <form>
        <input type="text">
        <button>Confirm</button>
    </form>

    <ul></ul>
</body>

 

  • index.js

state는 read-only 이기 때문에 직접 state를 수정하면 안 된다.

store의 state를 수정할 수 있는 유일한 방법은 action을 보내는 것이다.

state를 mutate 하는 것이 아니라, 새로운 state를 return 해야 한다. ex) push, pop을 통해서 배열을 직접 수정하면 안 된다.

import {createStore} from 'redux'

// html 태그 선택
const form = document.querySelector('form')
const input = document.querySelector('input')
const ul = document.querySelector('ul')

const ADD_TODO = "ADD_TODO"
const DELETE_TODO = "DELETE_TODO"

// 2. 리듀서 만들기
const reducer = (state = [], action) => {
  // 3. 액션 만들기
  switch(action.type) {
    case ADD_TODO:
      return [{ id: Date.now(), text: action.text }, ...state] // state.push(action.text) X
    case DELETE_TODO:
      return state.filter(toDo => toDo.id !== parseInt(action.id)) // filter(): 조건에 만족하는 리스트들을 새로운 배열로 return
    default:
      return state
  }
}

// 1. 스토어 만들기
const store = createStore(reducer)

// 투두 리스트 추가하기
const addToDo = text => {
  return {
    type: ADD_TODO,
    text
  }
}

// 투두 리스트 삭제하기
const deleteToDo = id => {
  return {
    type: DELETE_TODO,
    id
  }
}

// 4. 디스페치 만들기
const dispatchAddToDo = text => {
  store.dispatch(addToDo(text))
}

const dispatchDeleteToDo = e => {
  const id = parseInt(e.target.parentNode.id)
  store.dispatch(deleteToDo(id))
}

// 5. 이벤트 함수 만들기
const paintToDos = () => {
  const toDos = store.getState()

  // ul을 빈 값으로 만든 다음에 li를 추가시킨다. (새로운 return 값을 등록해야 하기 때문에 기존에 등록된 내용을 비운다.)
  ul.innerHTML = ''

  toDos.forEach(toDo => {
    const li = document.createElement('li')
    const btn = document.createElement('button')

    btn.innerText = 'DEL'
    btn.addEventListener('click', dispatchDeleteToDo) // 디스패치 실행

    li.id = toDo.id
    li.innerText = toDo.text

    li.appendChild(btn)
    ul.appendChild(li)
  })
}

// paintToDos의 변화를 감지한다.
store.subscribe(paintToDos)

// 5. 이벤트 함수 만들기
const onSubmit = e => {
  e.preventDefault()
  const toDo = input.value
  input.value = ''
  dispatchAddToDo(toDo) // 디스패치 실행
}

form.addEventListener('submit', onSubmit)

https://school.programmers.co.kr/learn/courses/30/lessons/70129

 

문제 설명

0과 1로 이루어진 어떤 문자열 x에 대한 이진 변환을 다음과 같이 정의합니다.

  1. x의 모든 0을 제거합니다.
  2. x의 길이를 c라고 하면, x를 "c를 2진법으로 표현한 문자열"로 바꿉니다.

예를 들어, x = "0111010"이라면, x에 이진 변환을 가하면 x = "0111010" -> "1111" -> "100" 이 됩니다.

0과 1로 이루어진 문자열 s가 매개변수로 주어집니다. s가 "1"이 될 때까지 계속해서 s에 이진 변환을 가했을 때, 이진 변환의 횟수와 변환 과정에서 제거된 모든 0의 개수를 각각 배열에 담아 return 하도록 solution 함수를 완성해주세요.

 

제한사항

  • s의 길이는 1 이상 150,000 이하입니다.
  • s에는 '1'이 최소 하나 이상 포함되어 있습니다.

 

toString()은 문자열을 반환하는 대표적인 방법이지만, 진법 변환할 때도 사용된다.

toString(2) = 2진법으로 변화, toSting(8) = 8진법으로 변환

function solution(s) {
    let zero = 0
    let one = 0
    let count = 0

    do {
        zero = zero + s.replaceAll(1, '').length // 1을 제거한 0의 길이
        one = s.replaceAll(0, '').length // 0을 제거한 1의 길이
        s = one.toString(2) // 2진법으로 바꾸기
        
        count++
    } while(s > 1)
        
    return [count, zero]
}

https://school.programmers.co.kr/learn/courses/30/lessons/12951

 

문제 설명

JadenCase란 모든 단어의 첫 문자가 대문자이고, 그 외의 알파벳은 소문자인 문자열입니다. 단, 첫 문자가 알파벳이 아닐 때에는 이어지는 알파벳은 소문자로 쓰면 됩니다. (첫 번째 입출력 예 참고)
문자열 s가 주어졌을 때, s를 JadenCase로 바꾼 문자열을 리턴하는 함수, solution을 완성해주세요.

 

제한조건

  • s는 길이 1 이상 200 이하인 문자열입니다.
  • s는 알파벳과 숫자, 공백문자(" ")로 이루어져 있습니다.
  • 숫자는 단어의 첫 문자로만 나옵니다.
  • 숫자로만 이루어진 단어는 없습니다.
  • 공백문자가 연속해서 나올 수 있습니다.

 

charAt(): 문자열에서 특정 인덱스에 위치하는 유니코드 단일문자를 반환한다.

function solution(s) {
    const arr = s.split(' ').map(list => list.toLowerCase())
    
    return arr.map(list => list.replace(list.charAt(0), list.charAt(0).toUpperCase())).join(' ')
}

+ Recent posts