[TIL] 230530

먼지·2023년 5월 30일
0

TIL

목록 보기
57/57
post-thumbnail

프로그래머스 코테입문 Day 25

Day 25 시뮬레이션, 조건문, 수학

문자열 밀기

문제설명
문자열 "hello"에서 각 문자를 오른쪽으로 한 칸씩 밀고 마지막 문자는 맨 앞으로 이동시키면 "ohell"이 됩니다. 이것을 문자열을 민다고 정의한다면 문자열 A와 B가 매개변수로 주어질 때, A를 밀어서 B가 될 수 있다면 밀어야 하는 최소 횟수를 return하고 밀어서 B가 될 수 없으면 -1을 return 하도록 solution 함수를 완성해보세요.

function solution(A, B) {
    if(A === B) return 0;
    let arr = [...A];
    for(let i=1; i<=A.length; i++) {
        arr.unshift(arr.pop())
        if(arr.join('') === B) return i;
    }
    return -1;
}

종이 자르기

문제설명
머쓱이는 큰 종이를 1 x 1 크기로 자르려고 합니다. 예를 들어 2 x 2 크기의 종이를 1 x 1 크기로 자르려면 최소 가위질 세 번이 필요합니다.

정수 M, N이 매개변수로 주어질 때, M x N 크기의 종이를 최소로 가위질 해야하는 횟수를 return 하도록 solution 함수를 완성해보세요.

function solution(M, N) {
    return M*N-1;
}

다음에 올 숫자

문제설명
등차수열 혹은 등비수열 common이 매개변수로 주어질 때, 마지막 원소 다음으로 올 숫자를 return 하도록 solution 함수를 완성해보세요.

function solution(common) {
    const [a, b, c, ...rest] = common;
    const lastNum = common[common.length - 1]
    if(b - a === c - b) {
        return lastNum + (b - a);
    } else {
        return lastNum * (b / a);
    }
}

연속된 수의 합

문제설명
연속된 세 개의 정수를 더해 12가 되는 경우는 3, 4, 5입니다. 두 정수 num과 total이 주어집니다. 연속된 수 num개를 더한 값이 total이 될 때, 정수 배열을 오름차순으로 담아 return하도록 solution함수를 완성해보세요.

function solution(num, total){
  const answer = [];
  const start = Math.ceil(total / num) - Math.floor(num / 2);
  const end = Math.floor(total / num) + Math.floor(num / 2);
  for(let i = start; i <= end; i++) answer.push(i);
  return answer;
}

// 다른 사람의 풀이
function solution(num, total) {
    var min = Math.ceil(total/num - Math.floor(num/2));
    var max = Math.floor(total/num + Math.floor(num/2));
    return new Array(max-min+1).fill(0).map((el,i)=>{return i+min;});
}

function solution(num, total) {
    let answer = []

    for(let i = 1; i <= num; i++) {
        answer.push(i)
    }

    const resultAv = parseInt(total / num)
    const resultAvIdx = num % 2 === 0 ? parseInt(num / 2) - 1 : parseInt(num / 2)
    const beforeV = answer[resultAvIdx]

    return answer.map(i => i = i + (resultAv - beforeV))
}

리액트 공식문서

state 구조 선택 - 도전 과제 4번 | 다중 선택 구현

Challenge 4 of 4: Implement multiple selection

배열의 includes 메소드를 사용해서 구현했는데 밑에 Set을 이용한 솔루션이 굿...

import { useState } from 'react';
import { letters } from './data.js';
import Letter from './Letter.js';

export default function MailClient() {
  const [selectedIds, setSelectedIds] = useState([]);
  const selectedCount = selectedIds.length;
  function handleToggle(toggledId) {
    if (selectedIds.includes(toggledId)) {
      setSelectedIds(selectedIds.filter(id =>
        id !== toggledId
      ));
    } else {
      setSelectedIds([
        ...selectedIds,
        toggledId
      ]);
    }
  }
  return (
    <>
      <h2>Inbox</h2>
      <ul>
        {letters.map(letter => (
          <Letter
            key={letter.id}
            letter={letter}
            isSelected={
              selectedIds.includes(letter.id)
            }
            onToggle={handleToggle}
          />
        ))}
        <hr />
        <p>
          <b>
            You selected {selectedCount} letters
          </b>
        </p>
      </ul>
    </>
  );
}

Solution

배열이 매우 큰 경우 includes()를 사용한 배열 검색은 선형 시간이 걸리고 개별 항목마다 이 검색을 수행하기 때문에 성능 문제가 될 수 있음.
이 문제를 해결하려면 대신 Set을 state를 사용하면 빠른 has() 연산을 제공함.

// App.js
import { useState } from 'react';
import { letters } from './data.js';
import Letter from './Letter.js';

export default function MailClient() {
  const [selectedIds, setSelectedIds] = useState(
    new Set()
  );

  const selectedCount = selectedIds.size;

  function handleToggle(toggledId) {
    // Create a copy (to avoid mutation).
    const nextIds = new Set(selectedIds);
    if (nextIds.has(toggledId)) {
      nextIds.delete(toggledId);
    } else {
      nextIds.add(toggledId);
    }
    setSelectedIds(nextIds);
  }

  return (
    <>
      <h2>Inbox</h2>
      <ul>
        {letters.map(letter => (
          <Letter
            key={letter.id}
            letter={letter}
            isSelected={
              selectedIds.has(letter.id)
            }
            onToggle={handleToggle}
          />
        ))}
        <hr />
        <p>
          <b>
            You selected {selectedCount} letters
          </b>
        </p>
      </ul>
    </>
  );
}

// Letter.js
export default function Letter({
  letter,
  onToggle,
  isSelected,
}) {
  return (
    <li className={
      isSelected ? 'selected' : ''
    }>
      <label>
        <input
          type="checkbox"
          checked={isSelected}
          onChange={() => {
            onToggle(letter.id);
          }}
        />
        {letter.subject}
      </label>
    </li>
  )
}

// data.js
export const letters = [{
  id: 0,
  subject: 'Ready for adventure?',
  isStarred: true,
}, {
  id: 1,
  subject: 'Time to check in!',
  isStarred: false,
}, {
  id: 2,
  subject: 'Festival Begins in Just SEVEN Days!',
  isStarred: false,
}];

컴포넌트 간의 state 공유

두 컴포넌트의 state가 항상 함께 변경되길 원한다면, 두 컴포넌트에서 state를 제거하고 가장 가까운 공통 부모로 이동한 다음 props를 통해 전달하면 됨. 이를 state 끌어올리기라고 함!

state 끌어올리기

한 패널의 버튼을 눌러도 다른 패널에 영향을 주지 않고 독립적으로 동작

import { useState } from 'react';

function Panel({ title, children }) {
  const [isActive, setIsActive] = useState(false);
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Show
        </button>
      )}
    </section>
  );
}

export default function Accordion() {
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel title="About">
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel title="Etymology">
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

한 번에 하나의 패널만 열리도록 변경하려면? 이 두 패널을 조정하기 위해 세 단계에 걸쳐 부모 컴포넌트로 "state를 끌어올려야" 함.

1. 자식 컴포넌트에서 state를 제거

Panel 컴포넌트에서 다음 줄을 제거

const [isActive, setIsActive] = useState(false);

대신 Panel의 props 목록에 isActive를 추가

function Panel({ title, children, isActive }) {

이제 Panel 컴포넌트는 isActive 값을 제어할 수 없고 부모 컴포넌트에 달려 있음!

2. 공통 부모 컴포넌트에 하드 코딩된 데이터를 전달

state를 끌어올리려면 조정하려는 두 자식 컴포넌트의 가장 가까운 공통 부모 컴포넌트를 찾아야 함
예제에서 가장 가까운 공통 부모는 Accordion 컴포넌트. 두 패널 위에 있고 props를 제어할 수 있으므로 현재 어떤 패널이 활성화되어 있는지에 대한 “진실 공급원(source of truth)”이 됨.

import { useState } from 'react';

export default function Accordion() {
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel title="About" isActive={true}>
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel title="Etymology" isActive={true}>
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

function Panel({ title, children, isActive }) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Show
        </button>
      )}
    </section>
  );
}

3. 공통 부모 컴포넌트에 state를 추가하고 이벤트 핸들러와 함께 전달

공통 부모 컴포넌트인 Accordion는 어떤 패널이 활성화된 패널인지 추적해야 함. boolean 값 대신, 활성화된 Panel 의 인덱스를 나타내는 숫자를 state 변수로 사용할 수 있음.

import { useState } from 'react';

export default function Accordion() {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel
        title="About"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel
        title="Etymology"
        isActive={activeIndex === 1}
        onShow={() => setActiveIndex(1)}
      >
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

function Panel({
  title,
  children,
  isActive,
  onShow
}) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={onShow}>
          Show
        </button>
      )}
    </section>
  );
}A single source of truth for each state

Controlled and uncontrolled components

  • 일반적으로 일부 로컬 state를 가진 컴포넌트를 “비제어 컴포넌트”라고 부름.
  • 반대로 컴포넌트의 중요한 정보가 자체 로컬 state가 아닌 props에 의해 구동되는 경우 컴포넌트가 “제어”된다고 말할 수 있음. 이렇게 하면 부모 컴포넌트가 그 동작을 완전히 지정할 수 있음.
  • 비제어 컴포넌트는 구성이 덜 필요하기 때문에 상위 컴포넌트 내에서 사용하기가 더 쉽지만 함께 통합하려는 경우 유연성이 떨어짐. 제어 컴포넌트는 최대한의 유연성을 제공하지만 부모 컴포넌트가 props를 사용하여 완전히 구성해야 함.
  • 컴포넌트를 작성할 때는 (props를 통해) 컴포넌트에서 어떤 정보를 제어해야 하는지, (state를 통해) 어떤 정보를 제어하지 않아야 하는지 고려하기. 하지만 나중에 언제든지 마음을 바꾸고 리팩토링할 수 있음

A single source of truth for each state

각 고유한 state들에 대해 해당 state를 “소유”하는 컴포넌트를 선택하게 됨. 이 원칙은 ”단일 진실 공급원“이라고도 함. 이는 모든 state가 한 곳에 있다는 뜻이 아니라, 각 state마다 해당 정보를 소유하는 특정 컴포넌트가 있다는 뜻. 컴포넌트 간에 공유하는 state를 복제하는 대신 공통으로 공유하는 부모로 끌어올려서 필요한 자식에게 전달함.

profile
꾸준히 자유롭게 즐겁게

0개의 댓글