[TIL] 230529

먼지·2023년 5월 29일
0

TIL

목록 보기
56/57
post-thumbnail

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

이제 하루 남았다,, 그동안 엄청 열심히는 아니지만 코딩테스트 문제에 익숙해지고 꾸준히 푸는 습관을 기르기 위해 바쁘거나 힘들지 않은 이상 최대한 매일 풀기 위해 노력했다. 그동안 풀었다는 게 눈에 보이니 확실히 효과가 커지는 것 같다. 솔직히 모두 답지없이 스스로 풀지 않았기에 조금만 뿌듯 ㅜㅜ 이제부터 레벨을 올려서 풀어보고 싶다. 내일이 마지막이니 최선을 다해보자!

Day 24
수학, 시뮬레이션, 문자열, 조건문, 반복문

치킨 쿠폰

문제 설명
프로그래머스 치킨은 치킨을 시켜먹으면 한 마리당 쿠폰을 한 장 발급합니다. 쿠폰을 열 장 모으면 치킨을 한 마리 서비스로 받을 수 있고, 서비스 치킨에도 쿠폰이 발급됩니다. 시켜먹은 치킨의 수 chicken이 매개변수로 주어질 때 받을 수 있는 최대 서비스 치킨의 수를 return하도록 solution 함수를 완성해주세요.

function solution(chicken) {
    let answer = 0;
    while(chicken >= 10){
    	answer += Math.floor(chicken / 10);
        chicken = chicken % 10 + Math.floor(chicken / 10);
    }
    return answer;
}

이진수 더하기

문제 설명
이진수를 의미하는 두 개의 문자열 bin1과 bin2가 매개변수로 주어질 때, 두 이진수의 합을 return하도록 solution 함수를 완성해주세요.

function solution(bin1, bin2) {
    return (parseInt(bin1, 2) + parseInt(bin2, 2)).toString(2);
}

// 다른 사람의 풀이
function solution(bin1, bin2) {
  let temp = Number(bin1) + Number(bin2);
  temp = [...temp.toString()].reverse().map((v) => +v);

  for (let i = temp.length; i < 11; i++) {
    temp.push(0);
  }

  for (let i = 0; i < temp.length; i++) {
    if (temp[i] === 2) {
      temp[i] = 0;
      temp[i + 1]++;
    } else if (temp[i] === 3) {
      temp[i] = 1;
      temp[i + 1]++;
    }
  }
  return Number(temp.reverse().join("")).toString();
}

parseInt()

  • 문자열 인자를 파싱하여 특정 진수(수의 진법 체계에서 기준이 되는 값)의 정수를 반환함
console.log(parseInt("10")); // 10
console.log(parseInt("10", 2)); // 2
console.log(parseInt("25", 2)); // NaN

toString()

  • 문자열을 반환하는 object의 대표적인 방법
  • 기수를 이용함으로써 10진수를 (1, 2, 3, 4, 5...) 다른 진수로 변환할 수 있음
let baseTenInt = 10;
console.log(baseTenInt.toString(2)); // "1010"이 출력됨

A로 B 만들기

문제 설명
문자열 before와 after가 매개변수로 주어질 때, before의 순서를 바꾸어 after를 만들 수 있으면 1을, 만들 수 없으면 0을 return 하도록 solution 함수를 완성해보세요.

function solution(before, after) {
    before = [...before].sort();
    after = [...after].sort();
    return before.filter((a, i) => a === after[i]).length === after.length ? 1 : 0;
}

k의 개수

문제 설명
1부터 13까지의 수에서, 1은 1, 10, 11, 12, 13 이렇게 총 6번 등장합니다. 정수 i, j, k가 매개변수로 주어질 때, i부터 j까지 k가 몇 번 등장하는지 return 하도록 solution 함수를 완성해주세요.

function solution(i, j, k) {
    let answer = "";
    for(let n=i; n<=j; n++) answer += String(n);
    return [...answer].filter(s => s.includes(k)).length;
}

// 다른 사람의 풀이
function solution(i, j, k) {
    let a ='';
    for(i;i<=j;i++){
        a += i;
    }
    return a.split(k).length-1;
}

리액트 공식문서 Choosing the State Structure

1. 관련 state 그룹화하기

두 개의 state 변수가 항상 함께 변경되는 경우엔 하나의 state 변수로 통합하는 것이 좋음

const [x, setX] = useState(0);  
const [y, setY] = useState(0);

// =>
const [position, setPosition] = useState({ x: 0, y: 0 });

데이터를 객체나 배열로 그룹화하는 또 다른 경우는 필요한 state의 조각 수를 모를 때. 예를 들어, 사용자가 사용자 정의 필드를 추가할 수 있는 양식이 있을 때 유용함.

2. state의 모순을 피하세요

“불가능한” state의 설정을 허용하고 있음

  • setIsSentsetIsSending 중 어느 하나만 호출하면 동시에 true가 되는 상황이 발생
    - 컴포넌트가 복잡할수록 무슨 일이 일어났는지 파악하기가 더 어려워짐
  • 세 가지 유효한 state 중 하나를 취할 수 있는 status라는 state변수 하나로 대체하기:
    statustypeing(초기), sending, sent가 될 수 있음
    - 가독성을 위해 일부 상수를 선언할 수 있음
    - state 변수가 아니므로 서로 동기화되지 않을까 걱정할 필요 없음
import { useState } from 'react';

export default function FeedbackForm() {
  // before
  // const [text, setText] = useState('');
  // const [isSending, setIsSending] = useState(false);
  // const [isSent, setIsSent] = useState(false);

  // async function handleSubmit(e) {
  //   e.preventDefault();
  //   setIsSending(true);
  //   await sendMessage(text);
  //   setIsSending(false);
  //   setIsSent(true);
  // }
  
  const [text, setText] = useState('');
  const [status, setStatus] = useState('typing');

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus('sending');
    await sendMessage(text);
    setStatus('sent');
  }

  const isSending = status === 'sending';
  const isSent = status === 'sent';

  if (isSent) {
    return <h1>Thanks for feedback!</h1>
  }

  return (
    <form onSubmit={handleSubmit}>
      <p>How was your stay at The Prancing Pony?</p>
      <textarea
        disabled={isSending}
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <br />
      <button
        disabled={isSending}
        type="submit"
      >
        Send
      </button>
      {isSending && <p>Sending...</p>}
    </form>
  );
}

// Pretend to send a message.
function sendMessage(text) {
  return new Promise(resolve => {
    setTimeout(resolve, 2000);
  });
}

3. 불필요한 state를 피하세요

렌더링 중에 컴포넌트의 props나 기존 state 변수에서 일부 정보를 계산할 수 있는 경우 해당 정보를 컴포넌트의 state에 넣지 않아야 함.

  • fullName은 불필요함. 렌더링 중 언제든 firstNamelastName에서 계산할 수 있으므로 state에서 제거!
    - fullName은 state 변수가 아님. 대신 렌더링 중에 계산됨
// before
export default function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [fullName, setFullName] = useState('');

  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
    setFullName(e.target.value + ' ' + lastName);
  }

  function handleLastNameChange(e) {
    setLastName(e.target.value);
    setFullName(firstName + ' ' + e.target.value);
  }
  ...
}
  
// after
export default function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');

  const fullName = firstName + ' ' + lastName;

  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
  }

  function handleLastNameChange(e) {
    setLastName(e.target.value);
  }
  ...
}

DEEP DIVE | 심층 탐구 - props를 state에 그대로 미러링하지 마세요

다음 코드는 중복 state의 일반적인 예시:

function Message({ messageColor }) {  
  const [color, setColor] = useState(messageColor);

문제는 부모 컴포넌트가 나중에 다른 messageColor 값(예: blue 대신 red)을 전달하면 color state 변수가 업데이트되지 않는다는 것! state는 첫 번째 렌더링 중에만 초기화됨.

대신 코드에서 messageColor prop을 직접 사용하기. 더 짧은 이름을 지정하려면 상수를 사용. 이렇게 하면 부모 컴포넌트에서 전달된 prop과 동기화되지 않음.

function Message({ messageColor }) {  
  const color = messageColor;

props를 state로 ‘미러링’하는 것은 특정 prop에 대한 모든 업데이트를 무시하려는 경우에만 의미있음. 관례에 따라 prop 이름을 initial 또는 default로 시작해 새 값이 무시됨을 명확히 하기.

function Message({ initialColor }) {  
  // The `color` state variable holds the *first* value of `initialColor`.  
  // Further changes to the `initialColor` prop are ignored.  
  const [color, setColor] = useState(initialColor);

4. state 중복을 피하세요

import { useState } from 'react';

const initialItems = [
  { title: 'pretzels', id: 0 },
  { title: 'crispy seaweed', id: 1 },
  { title: 'granola bar', id: 2 },
];

export default function Menu() {
  const [items, setItems] = useState(initialItems);
  const [selectedItem, setSelectedItem] = useState(
    items[0]
  );

  return (
    <>
      <h2>What's your travel snack?</h2>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.title}
            {' '}
            <button onClick={() => {
              setSelectedItem(item);
            }}>Choose</button>
          </li>
        ))}
      </ul>
      <p>You picked {selectedItem.title}.</p>
    </>
  );
}

현재 선택된 항목은 selectedItem state 변수에 객체로 저장됨 이것은 좋지 않음. selectedItem의 내용은 items 목록 내의 항목 중 하나와 동일한 객체로 즉, 항목 자체에 대한 정보가 두 곳에 중복됨.

selectedItem 객체(items 내부의 객체 복사본을 생성하는) 대신 selectedId를 state로 유지한 다음 items 배열에서 해당 ID를 가진 항목을 검색하여 selectedItem을 가져오기:

변경 전

  • items = [{ id: 0, title: 'pretzels'}, ...]
  • selectedItem = {id: 0, title: 'pretzels'}
    변경 후
  • items = [{ id: 0, title: 'pretzels'}, ...]
  • selectedId = 0

중복은 사라지고 필수 state만 유지됨

import { useState } from 'react';

const initialItems = [
  { title: 'pretzels', id: 0 },
  { title: 'crispy seaweed', id: 1 },
  { title: 'granola bar', id: 2 },
];

export default function Menu() {
  const [items, setItems] = useState(initialItems);
  const [selectedId, setSelectedId] = useState(0);

  const selectedItem = items.find(item =>
    item.id === selectedId
  );

  function handleItemChange(id, e) {
    setItems(items.map(item => {
      if (item.id === id) {
        return {
          ...item,
          title: e.target.value,
        };
      } else {
        return item;
      }
    }));
  }

  return (
    <>
      <h2>What's your travel snack?</h2>
      <ul>
        {items.map((item, index) => (
          <li key={item.id}>
            <input
              value={item.title}
              onChange={e => {
                handleItemChange(item.id, e)
              }}
            />
            {' '}
            <button onClick={() => {
              setSelectedId(item.id);
            }}>Choose</button>
          </li>
        ))}
      </ul>
      <p>You picked {selectedItem.title}.</p>
    </>
  );
}

5. 깊게 중첩된 state는 피하세요

중첩된 state를 업데이트하려면 변경된 부분부터 위쪽까지 객체의 복사본을 만들어야 함.
state가 너무 깊게 중첩되어 업데이트하기 어려운 경우 “flat(정규화)”하게 만드는 것을 고려하기

// before
export const initialTravelPlan = {
  id: 0,
  title: '(Root)',
  childPlaces: [{
    id: 1,
    title: 'Earth',
    childPlaces: [{
      id: 2,
      title: 'Africa',
      childPlaces: [{
        id: 3,
        title: 'Botswana',
        childPlaces: []
      }, {
        id: 4,
        title: 'Egypt',
        childPlaces: []
      }, {
        id: 5,
        title: 'Kenya',
        childPlaces: []
      }, {
        id: 6,
        title: 'Madagascar',
        childPlaces: []
      }, {
        id: 7,
        title: 'Morocco',
        childPlaces: []
      }, {
        id: 8,
        title: 'Nigeria',
        childPlaces: []
      }, {
        id: 9,
        title: 'South Africa',
        childPlaces: []
      }]
    }, {
      id: 10,
      title: 'Americas',
      childPlaces: [{
        id: 11,
        title: 'Argentina',
        childPlaces: []
      }, ...]
	}
  ]
}

// after
export const initialTravelPlan = {
  0: {
    id: 0,
    title: '(Root)',
    childIds: [1, 43, 47],
  },
  1: {
    id: 1,
    title: 'Earth',
    childIds: [2, 10, 19, 27, 35]
  },
  2: {
    id: 2,
    title: 'Africa',
    childIds: [3, 4, 5, 6 , 7, 8, 9]
  }, 
  3: {
    id: 3,
    title: 'Botswana',
    childIds: []
  },
  4: {
    id: 4,
    title: 'Egypt',
    childIds: []
  },
  5: {
    id: 5,
    title: 'Kenya',
    childIds: []
  },
  6: {
    id: 6,
    title: 'Madagascar',
    childIds: []
  }, 
  7: {
    id: 7,
    title: 'Morocco',
    childIds: []
  },
  ...
};

state를 원하는 만큼 중첩할 수 있지만 “flat”하게 만들면 많은 문제를 해결할 수 있고 state를 더 쉽게 업데이트할 수 있고 중첩된 객체의 다른 부분에 중복이 생기지 않도록 할 수 있음.

요약

  • 두 개의 state 변수가 항상 함께 업데이트되는 경우 두 변수를 하나로 병합하는 것이 좋음
  • state 변수를 신중하게 선택하여 ‘불가능한’ state를 만들지 않도록
  • state를 업데이트할 때 실수할 가능성을 줄이는 방식으로 state를 구성
  • 동기화 state를 유지할 필요가 없도록 불필요 및 중복 state를 피하기
  • 특별히 업데이트를 막으려는 경우가 아니라면 props를 state에 넣지 마라
  • 선택과 같은 UI 패턴의 경우 객체 자체 대신 ID 또는 인덱스를 state로 유지함
  • 깊게 중첩된 state를 업데이트하는 것이 복잡하다면 state를 평평하게 만들어 보기
profile
꾸준히 자유롭게 즐겁게

0개의 댓글