신나는 알고리즘 풀이 시간.
과일 장수가 사과 상자를 포장하고 있습니다. 사과는 상태에 따라 1점부터 k점까지의 점수로 분류하며, k점이 최상품의 사과이고 1점이 최하품의 사과입니다. 사과 한 상자의 가격은 다음과 같이 결정됩니다.
- 한 상자에 사과를 m개씩 담아 포장합니다.
- 상자에 담긴 사과 중 가장 낮은 점수가 p (1 ≤ p ≤ k)점인 경우, 사과 한 상자의 가격은 p * m 입니다.
과일 장수가 가능한 많은 사과를 팔았을 때, 얻을 수 있는 최대 이익을 계산하고자 합니다.(사과는 상자 단위로만 판매하며, 남는 사과는 버립니다)
예를 들어, k = 3, m = 4, 사과 7개의 점수가 [1, 2, 3, 1, 2, 3, 1]이라면, 다음과 같이 [2, 3, 2, 3]으로 구성된 사과 상자 1개를 만들어 판매하여 최대 이익을 얻을 수 있습니다.
- (최저 사과 점수) x (한 상자에 담긴 사과 개수) x (상자의 개수) = 2 x 4 x 1 = 8
사과의 최대 점수 k, 한 상자에 들어가는 사과의 수 m, 사과들의 점수 score가 주어졌을 때, 과일 장수가 얻을 수 있는 최대 이익을 return하는 solution 함수를 완성해주세요.
나의 답?
function solution(k, m, score) {
let answer = 0;
const copiedScore = [...score];
const boxArray = [];
let box = [];
copiedScore.sort((a, b) => a - b);
do {
for (let i = 0; i < m; i++) {
box.push(copiedScore.pop());
}
boxArray.push(box);
box = [];
} while (copiedScore.length >= m);
boxArray.forEach((applebox) => {
answer += Math.min(...applebox) * m;
});
return answer;
}
테스트 케이스 단 하나 빼고 전부 성공했다.
근데 왜 실패했는 지 이유를 모르겠다. 로직은 완벽한 거 같은데...
function solution(k, m, score) {
let answer = 0;
score.sort((a, b) => b - a);
for (let i = 0; i < score.length; i += m) {
if (i + m <= score.length) {
answer += score[i + m - 1] * m;
}
}
return answer;
}
gpt에서 추천해준 방법.
index만을 건드려서 문제를 해결하고 있다.
왜 전자는 안 되고 후자는 안 되는 것인가...이유를 알 수 없다.
일단 제출은 내가 짠 코드로 한 다음, 다시 짜야할 듯 하다.
개인 과제 프로그레스
table 렌더링
<div style={tableWrapper} id="tableWrapper">
<table>
<thead>
<tr>
<th>국가명</th>
<th>금메달</th>
<th>은메달</th>
<th>동메달</th>
<th>액션</th>
</tr>
</thead>
<tbody>
{medalLists.map((medalList, index) => {
return (
<tr key={index}>
<td>{medalList.country}</td>
<td>{medalList.goldMedal}</td>
<td>{medalList.silverMedal}</td>
<td>{medalList.copperMedal}</td>
<td>
<button
onClick={() => {
deleteBtn(index);
}}
>
삭제
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
저장된 데이터값을 통해 표를 렌더링하였다.
어떻게 각 행을 target으로 지정하나 싶었는데, 더 쉬운 방법이 있었다.
map을 할 때의 index를 참고하는 것이 바로 그 방법이다.
map을 만들 때 index를 만들어두면 각 엘리먼츠마다 순서가 부여되니, 이를 이용하면 index를 이용해서 대상을 쉽게 지정할 수 있다. map을 쓸 때 엘리먼츠만 따오는 걸 기억했는데, index도 자주 쓴다는 걸 알았다.
const deleteBtn = (index) => {
setMedalList(
medalLists.filter((l, i) => {
return i != index;
})
);
};
버튼을 삭제하는 함수. 인자로는 index를 받는다.
리액트에서 무언가를 수정하기 위해서는, 무조건 state를 이용해야함을 실감했던 때였다. state를 이용해서 메달 리스트에 필터링을 걸어, 선택된 인덱스가 아닌 인덱스만 리턴 하도록 하여, 삭제를 구현하였다.
그럼 이제 업데이트를 할 차례.
업데이트의 로직은 '같은 국가'를 입력했을 때에 발동한다.
즉, 이미 medalLists라는 배열 안에 같은 country가 있을 경우, 이를 업데이트할 수 있도록 해야한다.
업데이트 컴플리트.
const updateBtn = (e) => {
const revisedList = medalLists.map((list) => {
if (list.country === formData.country) {
return {
country: formData.country,
goldMedal: formData.goldMedal,
silverMedal: formData.silverMedal,
copperMedal: formData.copperMedal,
};
}
return list;
});
setMedalList(revisedList);
alert("업데이트 성공!");
};
업데이트의 감을 익혔다.
언제나 우리는 기존 데이터를 보존할 필요가 있다는 점을 상시 기억하자.
그렇기 때문에, 우리는 map을 이용해서 새로운 데이터리스트를 만들어, 이를 setMedalList에 반영해주는 식으로 업데이트 로직을 짤 것이다.
업데이트 버튼을 눌렀을 때, 리스트 안에 있는 나라가 폼데이터 안에 입력된 나라와 같을 시에, 해당 list의 값을 새 값으로 변경하는 로직을 짜서 리턴해주었다.
그리고 새롭게 수정된 리스트를 state를 반영해주면 성공이다.
컴뽀넌트 분리
<fieldset style={fieldsetStyle}>
<legend style={legendStyle}>{cellName}</legend>
<input
type="text"
name="country"
id="countryName"
onChange={onChangeHandler}
value={formData.country}
/>
</fieldset>
내 코드에선 이 부분이 계속 반복되는 문제가 있어서, 이 부분을 분리하여 새로은 jsx 파일을 파서 연동시키려 한다.
이걸 분리하려 할 때, 새롭게 생성된 jsx 파일에 존재하지 않는 데이터는 크게 세 가지
'onChangeHandler' 함수
formData 정보
마지막으로 스타일 정보이다.
그래서 부모 요소인 App.jsx에서 해당 요소들을 자식 요소로 내려줄 필요가 있다.
import React from "react";
const fieldsetStyle = {
margin: "10px",
border: "none",
};
const legendStyle = {
fontFamily: "Pretendard",
fontWeight: "800",
fontStyle: "normal",
textAlign: "center",
};
const Fieldset = ({ onChangeHandler, formData, cellName }) => {
let fieidsetName = "";
let inputValue;
if (cellName === "국가명") {
fieidsetName = "country";
inputValue = formData.country;
}
if (cellName === "금메달") {
fieidsetName = "goldMedal";
inputValue = formData.goldMedal;
}
if (cellName === "은메달") {
fieidsetName = "silverMedal";
inputValue = formData.silverMedal;
}
if (cellName === "동메달") {
fieidsetName = "copperMedal";
inputValue = formData.copperMedal;
}
return (
<fieldset style={fieldsetStyle}>
<legend style={legendStyle}>{cellName}</legend>
<input
type="text"
name={fieidsetName}
id={fieidsetName}
onChange={onChangeHandler}
value={inputValue}
/>
</fieldset>
);
};
export default Fieldset;
완성되어 분리된 컴포넌트.
cellName에 입력된 값에 따라, name에 부여되는 값이 달라지게 되어, 편하게 표를 간리할 수 있다. 이런 식으로 name을 달라지게 안 하면 input의 밸류값이 전부 통일되어버리는 불상사가 일어나기 때문에, 이렇게 따로 다 할당해줘야한다.
화면에 뭐가 잘 표시가 안 돼???
제대로 부모가 구현에 필요한 요소들을 자식들에게 뿌려주지 않아서 그런 경우일 확률이 높다.
언제나 함수를 체크하도록 하자.
그리고 차근차근 리팩토링이 진행되어서...
무려 이렇게까지 압축해내는데 성공했다!!!