0719 - 0725
0719
6일 차 JS 12 ~ 21회차 강의 정리
객체
- 어떠한 값을 선언할 때, 하나의 이름에 여러 종류의 값을 넣을 수 있게 해준다.
- 중괄호를 열고 그 안에
키: 값쌍으로 쉼표로 구분해서 넣는다. - 객체 안에 객체 삽입 가능
- 키에 공백이 있을 경우에는
''로 묶어줘야 동작한다. 객체변수.키를 콘솔로 찍어보면 키에 해당하는 값이 나온다.
비구조화 할당
- ES6에 있는 문법으로, 객체 구조 분해라고 불리기도 한다.
- 객체에서 특정 값들을 추출해내는 역할
function print(hero) {
const { alias, name, actor } = hero;
const text = `${alias}(${name}) 역할을 맡은 배우는 ${actor} 입니다.`;
console.log(text);
}
- 파라미터에
{ alias, name, actor }를 써도 정상 작동한다. - 꼭 함수 내부에 사용할 필요는 없다. 변수로 선언해서 사용할 수도 있다.
- ex)
const { name } = ironMan;
- ex)
객체 안에 함수 넣기
const dog = {
name: "멍멍이",
sound: "멍멍!",
say: function say() {
console.log(this.sound);
},
};
say: function say()를say: function()또는say()로 바꿔줘도 정상 작동한다.- 여기서의
this는 속해있는 곳으로 연결한다. - 여기서 만든 함수를 꺼내게 되면 this와의 관계는 사라진다.
- 화살표 함수로 만들게 된다면, this가 뭔지 모르기 때문에 작동하지 않는다.
Getter와 Setter함수
- 객체 안에 Getter함수와 Setter함수를 설정할 수 있다.
- Getter함수는 특정 값을 조회할 때마다 실행된다.
- Setter함수는 특정 값을 설정할 때마다 실행된다.
- Getter함수를 만들 때는 앞에 키워드 get을 붙이고 함수를 선언하면 된다. 반드시 어떤 값을 반환해줘야 한다.
- Setter함수를 만들 때는 앞에 키워드 set을 붙이고 함수를 선언하면 된다. 단 여기서 파라미터를 필수로 넣어줘야 한다.
const numbers = {
a: 1,
b: 2,
get sum() {
console.log("sum 함수가 실행됩니다!");
return this.a + this.b;
},
};
console.log(numbers.sum);
numbers.a = 5;
console.log(numbers.sum);
- getter 함수는 특정 값을 호출하는 것이 아니라 ex)
sum()조회하려 할 때 특정 코드를 실행시키고 값을 반환하는 코드- 오히려 함수를 호출하듯이 하면 에러가 발생한다.
const dog = {
_name: "멍멍이",
get name() {
console.log("_name을 조회합니다..");
return this._name;
},
set name(value) {
console.log("이름이 바뀝니다.." + value);
this._name = value;
},
};
console.log(dog.name);
dog.name = "뭉뭉이";
console.log(dog.name);
- 여기서
_name으로 키를 작명한 것은 getter, setter와 이름이 같으면 안되기 때문이다. - 다만 getter와 setter의 이름은 같아도 된다.
- 위와 같이 작성하면,
dog.name으로_name값을 가져올 수 있다.
배열
- 여러 개의 항목들이 들어있는 리스트
- 자바스크립트에서는 한 배열에서 타입이 꼭 같을 필요가 없다.
- 대괄호
[]로 선언한다. - 0부터 시작한 인덱스로 배열에 접근 가능하다.
push
배열변수.push(값)을 사용해 배열의 끝에 값을 삽입할 수 있다.
length
배열변수.length는 배열의 길이를 반환한다.- 주의할 점은 함수가 아니라 실행하면 에러가 발생한다.
반복문
- 시작
- 조건이 true이면 구문 실행, false이면 끝
- 구문을 실행하고, 다시 조건을 확인. 조건이 false가 될 때까지 반복
for
for (let i = 0; i < 10; i++) {
console.log(i);
}
- 가장 기본적인 반복문 형태인 for 문
- 0부터 9까지 1씩 증가하면서 콘솔에 찍는다.
- 1씩 작아지도록 작성하거나, 2씩 증가하도록 작성할 수도 있다.
- 배열과 조합하여 배열 내부의 값들을 인덱스로 꺼내올 수 있다.
while
let i = 0;
while (i < 10) {
console.log(i);
i++;
}
- for 문과 다른 점은 초깃 값을 while문 바깥에 한다는 것과, 선언부에 증감식을 준다는 것이다.
- 무한 루프를 돌지 않도록 조건을 잘 줘야 한다.
for…of
const array = [1, 2, 3, 4];
for (let num of array) {
console.log(num);
}
// 결괏값
1;
2;
3;
4;
for...of는 배열 안에 있는 것들을 반복적으로 사용해서 작업해야 할 때 쓴다.
for…in
const dog = {
name: "멍멍이",
sound: "멍멍",
age: 2,
};
for (let key in dog) {
console.log(`${key}: ${dog[key]}`);
}
for...in은 객체에 대한 반복적인 작업을 처리해야 할 때 쓴다.- key 변수에는 dog 객체의 모든 키가 담기는데, template literal을 사용해서 dog 객체의 값을 받아올 수도 있다.
Object 객체 내장함수
const dog = {
name: "멍멍이",
sound: "멍멍",
age: 2,
};
console.log(Object.entries(dog));
console.log(Object.keys(dog));
console.log(Object.values(dog));
// 결괏값
[
["name", "멍멍이"],
["sound", "멍멍"],
["age", 2],
][("name", "sound", "age")][("멍멍이", "멍멍", 2)];
Object.entries()는 객체의 키, 값 쌍 2차원 배열 형태로 반환한다.Object.keys()는 객체의 모든 키를 배열로 반환한다.Object.values()는 객체의 모든 값을 배열로 반환한다.
break와 continue
- continue를 반복문 내에서 만나게 되면 바로 증감식으로 넘어가서 다음 루프를 돌게 된다.
- break 문을 만나게 되면 반복문이 아예 끝난다.
0720
7일 차 JS 22 ~ 29회차 강의 정리
배열 내장함수
forEach
- 배열의 각 요소들에서 특정 동작을 수행한다.
- 매개변수로 콜백함수를 받는다.
- 배열의 값 뿐만 아니라 인덱스에도 접근할 수 있다.
const heroes = ['아이언맨', '캡틴 아메리카', '토르', '닥터 스트레인지'];
heroes.forEach((hero, index) => {
console.log(hero, index);
})
// 결괏값
아이언맨 0
캡틴 아메리카 1
토르 2
닥터 스트레인지 3
map
- 배열 안의 원소를 변환할 때 사용
const items = [
{
id: 1,
text: "hello",
},
{
id: 2,
text: "bye",
},
];
const texts = items.map((item) => item.text);
console.log(texts);
// 결괏값
["hello", "bye"];
indexOf
- 해당하는 값이 배열 안에서 첫 번째로 발견된 인덱스를 반환한다.
- 값을 배열에서 찾지 못하면 -1을 반환
const superheroes = ["아이언맨", "캡틴 아메리카", "토르", "닥터 스트레인지"];
const index = superheroes.indexOf("토르");
console.log(index);
// 결괏값
2;
findIndex
- 배열 안에 있는 값들이 객체이거나, 특정 조건을 확인해야 하는 경우 사용
const todos = [
{
id: 1,
text: "자바스크립트 입문",
done: true,
},
{
id: 2,
text: "함수 배우기",
done: true,
},
{
id: 3,
text: "객체와 배열 배우기",
done: true,
},
{
id: 4,
text: "배열 내장함수 배우기",
done: false,
},
];
const index = todos.findIndex((todo) => todo.id === 3);
console.log(index);
// 결괏값
2;
findIndex()는 인덱스를 반환해준다면,find()는 찾은 객체 자체를 반환해준다.
filter
- 특정 배열에서 특정 조건을 만족하는 원소들만 추출해서 새로운 배열을 만들 때 사용
const taskNotDone = todos.filter((todo) => !todo.done);
console.log(taskNotDone);
// 결괏값
[{ id: 4, text: "배열 내장함수 배우기", done: false }];
- 기존의 배열은 건드리지 않고 새로운 배열을 생성한다.
splice, slice
- splice는 매개변수로 받는 인덱스에 해당하는 값을 삭제한다.
- 첫 번째 매개변수에는 삭제하고자 하는 인덱스, 두 번째 매개변수에는 그 인덱스부터 시작하여 몇개를 지울 것인지를 받는다.
- splice의 결괏값으로 삭제한 배열을 반환한다.
- slice와 splice의 차이점은 slice는 기존 배열은 건드리지 않고, 잘라낸 배열만 반환한다는 것이다.
const numbers = [10, 20, 30, 40];
const index = numbers.indexOf(30);
console.log(index);
const spliced = numbers.splice(index, 1);
console.log(numbers);
console.log(spliced);
// 결괏값
(2)[(10, 20, 40)][30];
shift, unshift, pop, push
- shift는 배열에서 가장 앞에 있는 원소를 배열에서 꺼내고 반환해준다.
- pop은 배열에서 가장 뒤에 있는 원소를 배열에서 꺼내고 반환해준다.
- unshift는 배열에서 가장 앞에 값을 삽입한다.
- push는 배열의 맨 뒤에 값을 삽입한다.
concat
- 기존의 배열을 건드리지 않고, 두 배열을 합쳐서 새로운 배열을 반환한다.
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const concated = arr1.concat(arr2);
console.log(concated);
// 결괏값
[1, 2, 3, 4, 5, 6];
join
- 배열을 문자열로 변환해준다.
- separate를 매개변수로 받는다. 기본 값은 ,
const array = [1, 2, 3, 4, 5];
console.log(array.join());
// 결괏값
1, 2, 3, 4, 5;
reduce
- 배열 안의 모든 값들을 사용해서 연산해야 할 때 사용
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, current) => acc + current, 0);
console.log(sum);
- 첫 번째 매개변수에는 함수를 넣게 되는데, 그 함수는 기본적으로 값을 저장할 accumulator과 현재 배열의 값을 가지는 current를 갖는다.
- 두 번째 매개변수에는 값을 저장할 accumulator의 초기값을 설정한다.
- acc를 초기화하고, 함수에서 배열의 값과 필요한 연산을 한 뒤 다시 그 값을 acc에 저장하는 것을 반복한다.
- 첫 번째 매개변수에서 acc과 current 뒤에 배열의 index 값과 자기 자신 배열을 가리키는 array를 가져올수도 있다.
const alphabets = ['a', 'a', 'a', 'b', 'c', 'c', 'd', 'e'];
const counts = alphabets.reduce((acc, current) => {
if (acc[current]) {
acc[current] += 1;
} else {
acc[current] = 1;
}
return acc;
}, {})
console.log(counts);
// 결괏값
{ a: 3, b: 1, c: 2, d: 1, e: 1 }
- 위와 같이 알파벳의 개수를 구할수도 있다.
0721
8일 차 JS 1 ~ 6회차 강의 정리 및 알고리즘
오늘부터 자바스크립트 온라인 강의와 더불어 알고리즘 과제를 해결하기로 했다. 난이도는 기업 코딩테스트 기준으로 봤을 때는 상당히 쉬운 문제들이지만, 문제를 풀면서 새로 알게된 개념이나 유용한 코딩을 간단하게나마 정리해보고자 한다.
알고리즘
중복 문자 제거
소문자로 된 한개의 문자열이 입력되면 중복된 문자를 제거하고 출력하는 프로그램을 작성하세요.
제거된 문자열의 각 문자는 원래 문자열의 순서를 유지합니다.
function solution(str) {
let answer = "";
for (let i = 0; i < str.length; i++) {
if (answer.indexOf(str.charAt(i)) === -1) {
answer += str.charAt(i);
}
}
return answer;
}
console.log(solution("ksekkset"));
- 주어진 문자열을 단어별로 반복문을 돌려서 빈 문자열에 해당하는 단어가 있는지 확인하여 없으면 해당 단어를 추가하여 결과를 출력했다.
회문 문자열
앞에서 읽을 때나 뒤에서 읽을 때나 같은 문자열을 회문 문자열이라고 합니다.
문자열이 입력되면 해당 문자열이 회문 문자열이면 “YES”, 회문 문자열이 아니면 “NO”를 출력 하는 프로그램을 작성하세요.
단 회문을 검사할 때 대소문자를 구분하지 않습니다.
function solution(str) {
let answer = "";
const lowerStr = str.toLowerCase();
const reversedStr = lowerStr.split("").reverse().join("");
if (lowerStr === reversedStr) {
answer = "YES";
} else {
answer = "NO";
}
return answer;
}
console.log(solution("gooG"));
console.log(solution("gattBG"));
- 우선 대소문자를 구분하지 않기 위해서 문자열을 소문자로 통일하기 위해 변환하였다.
split('').reverse().join('')를 사용해 문자열을 배열로 바꾸고, 배열 함수로 순서를 거꾸로 뒤집어서 다시 문자열로 만들었다.- 기존 문자열과 뒤집은 문자열을 비교하여 결과를 반환하였다.
뒤집은 소수
N개의 자연수가 입력되면 각 자연수를 뒤집은 후 그 뒤집은 수가 소수이면 그 소수를 출력하는 프로그램을 작성하세요.
예를 들어 32를 뒤집으면 23이고, 23은 소수이다. 그러면 23을 출력한다. 단 910를 뒤집으면 19로 숫자화 해야 한다. 첫 자리부터의 연속된 0은 무시한다.
function solution(array) {
const answer = [];
array.forEach((num) => {
const reversed = parseInt(num.toString().split("").reverse().join(""));
if (reversed === 1) {
return;
}
for (let i = 2; i < reversed; i++) {
if (reversed % i === 0) {
return;
}
}
answer.push(reversed);
});
return answer;
}
console.log(solution([32, 55, 62, 20, 250, 370, 200, 30, 100]));
- 숫자 배열을 반복문을 사용해서 문자열로 바꾼 뒤,
split('').reverse().join('')으로 뒤집고 다시 숫자형으로 변하게 해서 첫 자리부터 연속된 0을 무시하면서 수를 뒤집었다. - 뒤집은 수가 1이면 소수가 아니기에 return 시켰고, 2부터 자기 자신 수까지 나누어 떨어지는 수가 있으면 소수가 아니기에 return 시켰다. 그 외에는 모두 정답 배열에 push 시켜서 결과를 도출하였다.
괄호문자제거
입력된 문자열에서 소괄호 ( ) 사이에 존재하는 모든 문자를 제거하고 남은 문자만 출력하는 프로그램을 작성하세요.
function solution(str) {
let answer = "";
let stack = [];
let array = str.split("");
array.forEach((element) => {
if (element === ")") {
stack.splice(stack.lastIndexOf("("));
} else {
stack.push(element);
}
});
answer = stack.join("");
return answer;
}
console.log(solution("(A(BC)D)EF(G(H)(IJ)K)LM(N)"));
forEach로 배열을 돌리기 위해 문자열을 배열로 변환하였다.- 반복문을 사용해서 요소가 ‘(‘나 일반 문자라면 빈 스택에 push 하였고, ‘)’라면 뒤에서부터 ‘(‘의 인덱스를 찾아서 해당 인덱스부터 맨 뒤까지의 모든 요소를 제거하였다.
- 배열을
join함수로 문자열로 변환하여 결과를 출력하였다.
재귀함수를 이용한 이진수 출력
10진수 n이 입력되면 2진수로 변환하여 출력하는 프로그램을 작성하세요. 단 재귀함수를 이용 해서 출력해야 합니다.
function solution(num) {
let answer = "";
let devided = parseInt(num / 2);
if (devided === 1) {
answer += devided;
answer += num % 2;
} else {
answer = solution(devided);
answer += num % 2;
}
return answer;
}
console.log(solution(11));
console.log(solution(20));
- 받아온 숫자를 2로 나눠서 몫을 구한 값을 변수에 담았다.
- 몫이 1이면 더 이상 나눌 필요가 없기에 빈 문자열 answer에 몫과 나머지를 순서대로 더했다.
- 몫이 1이 아니라면 계속 나눠야 하기 때문에 나올 때까지 재귀 호출하여 반환된 값을 answer에 저장했다.
- 순차적으로 호출이 마친 뒤에 해당 블록의 나머지 값을 문자열에 순차적으로 더해서 재귀호출을 사용해 이진수를 구했다.
- 자바에서는 몫을 구할 때 두 수 모두 자연수이면 결과 값도 몫만 도출되었는데, 자바스크립트에서는 소수로 구해진다는 사실을 처음 알게 되었다. 이 점을 유의해서 코딩해야겠다고 생각했다.
- 재귀호출은 쉬우면서도 머리가 너무 아프다. 이 부분을 잘 정리해서 더 어려운 문제를 맞닥트렸을 때, 자유자재로 사용할 수 있게 실력을 키워야겠다.
JS 강의 정리
삼항 연산자
condition ? true : false
if ~ else문의 조금 더 편하게 쓰는 문법이라고 할 수 있다.- condition 내의 값이 true 이면 ? 뒤의 문장이 실행되고, false이면 : 뒤의 문장이 실행된다.
- 삼항 연잔자를 연속으로 중첩해서 사용하는 것은 가독성이 떨어질 수 있으므로, 그런 상황에서는 if 문으로 대체한다.
Truthy와 Falsy
- Truthy는 True 같은 것. Falsy는 False 같은 것.
- Falsy한 값의 대표적인 5개의 값
- undefined
- null
- 0
- ’’
- NaN
- 그 외에는 모두 Truthy한 값
- 조건문에서 잘 활용할 수 있다.
console.log(!undefined);
console.log(!0);
console.log(!null);
console.log(!"");
console.log(!NaN);
// 결괏값
true;
true;
true;
true;
true;
- 예를 들어 변수가 Falsy한 값일 때, 방어 코드를 작성하는 곳에 활용할 수 있다.
- 또는 변수 값이 Truthy한 값일 때만 조건문 블럭을 실행하는 방식으로도 사용할 수 있다.
단축 평가 논리 계산법
- 논리 연산자를 사용해서 코드를 더 짧게 쓰는 방법
console.log(true && "hello");
console.log(false && "hello");
console.log("hello" && "bye");
console.log(null && "hello");
console.log(undefined && "hello");
console.log("" && "hello");
console.log(1 && "hello");
// 결괏값
hello;
false;
bye;
null;
undefined;
hello;
&&연산자는 앞에 있는 값이 Truthy 한 값이면 뒤의 값이 나오고, 앞에 있는 값이 Falsy한 값이면 앞의 값이 나온다.- 특정 값이 유효할 때만 특정 값을 조회해야 하는 상황
||연산자는 앞에 있는 값이 Truthy 한 값이면 앞의 값이 나오고, 앞의 값이 Falsy한 값이면 뒤의 값이 나온다.- 어떤 값이 없을 때, 값을 지정해 줄 때 사용하면 유용하다.
함수의 기본 파라미터
- 함수를 호출할 때 인자를 주지 않았을 경우에 기본 파라미터 값을 설정할 수 있다.
function calculate(r = 1) {
return Math.PI * r * r;
}
console.log(calculate());
// 결괏값
3.141592653589793;
- 매개변수에
=기호 우측에 값을 입력하면 그것이 해당 매개변수의 값을 전달받지 못했을 때 기본값이 된다. - 화살표 함수에서도 같은 방식으로 기본 파라미터를 지정할 수 있다.
조건문 더 스마트하게 쓰기
특정 값이 여러 값 중 하나인지 확인해야 하는 상황
function isAnimal(text) {
return (
text === "고양이" || text === "개" || text == "거북이" || text === "너구리"
);
}
console.log(isAnimal("개"));
console.log(isAnimal("노트북"));
- 비교하고자 하는 값을 배열로 선언해서 내장함수 includes로 보기 좋게 작성할 수 있다.
const isAnimal = (text) => ["고양이", "개", "거북이", "너구리"].includes(text);
주어진 값에 따라 다른 결과물을 반환해야 하는 상황
function getSound(animal) {
if (animal === "개") return "멍멍!";
if (animal === "고양이") return "야옹~";
if (animal === "참새") return "짹짹";
if (animal === "비둘기") return "구구 구 구";
return "...?";
}
console.log(getSound("개"));
console.log(getSound("비둘기"));
console.log(getSound("인간"));
switch ~case문으로 바꿀수도 있으나 그렇게 했을 경우는 위의 if문과 큰 차이가 없다.- 객체를 활용해서 더 깔끔하게 구현할 수 있다.
function getSound(animal) {
const sounds = {
개: "멍멍!",
고양이: "야옹~",
참새: "짹짹",
비둘기: "구구 구 구",
};
return sounds[animal] || "...?";
}
console.log(getSound("개"));
console.log(getSound("비둘기"));
console.log(getSound("인간"));
- 객체의 키 값과 단축 평가 논리 계산법을 이용해서 깔끔하게 구현하였다.
주어진 값에 따라 다른 결과물을 실행해야 하는 상황
- 위의 주어진 값에 따라 다른 결과물을 반환해야 하는 상황과 비슷하다.
function makeSound(animal) {
const tasks = {
개: () => console.log("멍멍!"),
고양이() {
console.log("야옹~");
},
비둘기() {
console.log("구구 구 구");
},
};
if (!tasks[animal]) {
console.log("...?");
return;
}
tasks[animal]();
}
makeSound("개");
makeSound("비둘기");
makeSound("인간");
비구조화 할당
- 비구조화 할당에서도 함수의 기본 파라미터와 같은 방식으로 기본 값을 설정할 수 있다.
const object = { a: 1 };
const { a, b = 2 } = object;
console.log(a);
console.log(b);
// 결괏값
1;
2;
- 비구조화 할당을 할 때 이름을 바꿀 수도 있다.
const animal = {
name: "멍멍이",
type: "개",
};
const { name: nickname } = animal;
console.log(nickname);
// 결괏값
멍멍이;
-
animal이 가지고 있던 name 값이 바뀌는 것은 아니다.
-
배열에서도 비구조화 할당을 사용할 수 있다.
const array = [1, 2];
const [one, two = 2] = array;
console.log(one);
console.log(two);
- 배열의 요소를 변수에 쉽게 할당할 수 있는 방법이다.
-
객체의 비구조화 할당과 동일하게 기본값을 설정할 수 있다.
- 객체 깊숙한 곳에 있는 것을 비구조화 할당할 수 있다.
const deepObject = {
state: {
information: {
name: 'junghoon',
languages: ['korean', 'english']
}
},
value: 5
}
const { name, languages } = deepObject.state.information;
const { value } = deepObject;
const extracted = {
name,
languages,
value
};
console.log(extracted);
// 결괏값
{ name: 'junghoon', languages: [ 'korean', 'english' ], value: 5 }
- depth가 다른 값을 비구조화 할당으로 꺼내오는 방법으로는 크게 두 가지 방법이 있는데 첫 번째는 위와 같이 비구조화 할당을 두 번 사용하는 것이다.
-
extracted는 값을 설정하는 것을 생략했는데, 이미 키에 값이 있기 때문에 생략해도 된다.
- 두 번째 방법은 비구조화 할당을 한 번 하면서 여러 값을 다 빼오는 것이다.
const {
state: {
information: { name, languages },
},
value,
} = deepObject;
- 가독성이 떨어진다는 단점이 있다.
0722
9일 차 JS 7 ~ 12회차 강의 정리 및 알고리즘
알고리즘
대문자 찾기
한 개의 문자열을 입력받아 해당 문자열에 알파벳 대문자가 몇 개 있는지 알아내는 프로그램을 작성하세요.
function solution(str) {
let answer = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] === str[i].toUpperCase()) {
answer++;
}
}
return answer;
}
console.log(solution("KoreaTimeGood"));
- 문자열을 조회해서 대문자로 변경한 값과 기존의 값이 동일한지 체크하고, 동일한 수를 세서 반환하였다.
대소문자 변환
대문자와 소문자가 같이 존재하는 문자열을 입력받아 대문자는 소문자로 소문자는 대문자로 변환하여 출력하는 프로그램을 작성하세요.
function solution(str) {
let answer = "";
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === str.charAt(i).toUpperCase()) {
answer += str.charAt(i).toLowerCase();
} else {
answer += str.charAt(i).toUpperCase();
}
}
return answer;
}
console.log(solution("StuDY"));
console.log(solution("gattBG"));
- 대문자 찾기 문제와 비슷한 방법을 사용해서, 문자가 대문자라면 소문자로 바꾸고 아니라면 소문자이므로 대문자로 변환해서 반환하였다.
가위 바위 보
A, B 두 사람이 가위바위보 게임을 합니다. 총 N번의 게임을 하여 A가 이기면 A를 출력하고, B가 이기면 B를 출력합니다. 비길 경우에는 D를 출력합니다.
가위, 바위, 보의 정보는 1:가위, 2:바위, 3:보로 정하겠습니다.
두 사람의 각 회의 가위, 바위, 보 정보가 주어지면 각 회를 누가 이겼는지 출력하는 프로그램을 작성하세요.
function solution(aNum, bNum) {
let answer = [];
for (let i = 0; i < aNum.length; i++) {
let info = aNum[i] - bNum[i];
if (info === 0) answer.push("D");
else if (info === 1 || info === -2) answer.push("A");
else answer.push("B");
}
return answer;
}
console.log(solution([2, 3, 3, 1, 3], [1, 1, 2, 2, 3]));
- 각 판마다 서로 낸 값을 빼면 이겼을 때의 경우를 계산할 수 있다.
- 계산한 값을 사용해서 정답 배열에 비기면 D, A가 이기면 A, B가 이기면 B를 push해서 해결하였다.
격자판 최대합
N*N의 격자판이 주어지면 각 행의 합, 각 열의 합, 두 대각선의 합 중 가 장 큰 합을 출력합니다.
function solution(arr) {
let answer = 0;
let sum1 = 0;
let sum2 = 0;
let sum3 = 0;
let sum4 = 0;
let sumArr = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr[i].length; j++) {
sum1 += arr[i][j];
sum2 += arr[j][i];
}
sumArr.push(sum1);
sumArr.push(sum2);
sum1 = 0;
sum2 = 0;
sum3 += arr[i][i];
sum4 += arr[i][arr.length - 1 - i];
}
sumArr.push(sum3);
sumArr.push(sum4);
sumArr.forEach((element) => {
if (element >= answer) {
answer = element;
}
});
return answer;
}
console.log(
solution([
[10, 13, 10, 12, 15],
[12, 39, 30, 23, 11],
[11, 25, 50, 53, 15],
[19, 27, 29, 37, 27],
[19, 13, 30, 13, 19],
])
);
- 꽤 어려웠다고 느꼈던 문제이다. 예전에 자바 공부를 하면서 배열을 2차원 인덱스로 적어 나타내서 규칙을 찾는 방법이 생각나서 그 방법으로 풀었다.
- 왠지 sum1~4 까지 변수를 너무 많이 선언한 것은 아닌가 하는 아쉬움이 드는 문제였다.
- 각 가로, 세로, 대각선의 합을 한 배열에 넣고 그 배열 내에서 최대값을 반환해서 해결했다.
문자열 압축
알파벳 대문자로 이루어진 문자열을 입력받아 같은 문자가 연속으로 반복되는 경우 반복되는 문자 바로 오른쪽에 반복 횟수를 표기하는 방법으로 문자열을 압축하는 프로그램을 작성하시오. 단 반복횟수가 1인 경우 생략합니다.
function solution(str) {
let answer = [];
let arr = str.split("");
let cntArr = {};
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== arr[i + 1]) {
answer.push(arr[i]);
} else {
cntArr[arr[i]] = (cntArr[arr[i]] || 1) + 1;
}
}
for (let i = 0; i < answer.length; i++) {
if (cntArr[answer[i]]) {
answer.splice(i + 1, 0, cntArr[answer[i]]);
}
}
answer = answer.join("");
return answer;
}
console.log(solution("KKHSSSSSSSE"));
console.log(solution("KSHHEAAA"));
- 문자열을 배열로 바꾸고 반복문을 돌려서 현재 인덱스에 위치한 문자가 다음 인덱스에 위치한 문자랑 다르다면 정답 배열에 push하고 같다면 해당 문자가 몇 번이나 나왔는지 저장하는 객체에 담았다.
- 객체에 담을 때
+=연산자를 사용하려 했는데 NaN이 나왔다.- 숫자로 초기화되지 않은 영역에
+=를 사용할 수 없다는 것을 알게 되었고, 최근에 배운 단축 평가 논리 계산법을 이용해서 해결하였다.
- 숫자로 초기화되지 않은 영역에
- 정답 배열에 해당 문자의 반복 횟수를 객체에서 꺼내서 삽입하고, 문자열로 다시 변환해서 반환하였다.
멘토링
현수네 반 선생님은 반 학생들의 수학점수를 향상시키기 위해 멘토링 시스템을 만들려고 합니다 . 멘토링은 멘토(도와주는 학생)와 멘티(도움을 받는 학생)가 한 짝이 되어 멘토가 멘티의 수학공부를 도와주는 것입니다.
선생님은 M번의 수학테스트 등수를 가지고 멘토와 멘티를 정합니다.
만약 A학생이 멘토이고, B학생이 멘티가 되는 짝이 되었다면, A학생은 M번의 수학테스트에서 모두 B학생보다 등수가 앞서야 합니다.
M번의 수학성적이 주어지면 멘토와 멘티가 되는 짝을 만들 수 있는 경우가 총 몇 가지 인지 출력하는 프로그램을 작성하세요.
function solution(nums) {
let answer = 0;
const testCnt = nums.length;
const stuCnt = nums[0].length;
// 학생의 수가 n명이면 n * n가지의 경우의 멘토, 멘티 수가 나온다.
for (let i = 1; i <= stuCnt; i++) {
for (let j = 1; j <= stuCnt; j++) {
// 멘토랑 멘티가 같은 학생일 수는 없으므로 패스
if (i === j) continue;
let cnt = 0;
for (let k = 0; k < testCnt; k++) {
if (nums[k].indexOf(i) < nums[k].indexOf(j)) {
cnt = 1;
} else {
cnt = 0;
break;
}
}
answer += cnt;
}
}
return answer;
}
console.log(
solution([
[3, 4, 1, 2],
[4, 3, 2, 1],
[3, 1, 4, 2],
])
);
- 가장 어려웠고, 1시간 이상 붙들고 있다가 안되겠어서 다른 곳에서 힌트를 조금 얻어서 겨우 해결했다.
- 이러한 문제 유형을 브루트 포스(brute force)라고 한다는데, 완전 탐색 알고리즘. 가능한 모든 경우의 수를 탐색하면서 요구조건에 충족하는 결과만 가져오는 유형이라고 한다.
- 총 3번의 반복문을 이용했는데, 반복문을 2중 이상으로 사용하면 안된다는 고정관념이 있어서 이 문제를 해결하는데 엄청 헤맨 것 같다.
- 멘토랑 멘티가 짝을 이룰 수 있는 경우의 수를 구하기 위해 2중 반복문을 사용했고, 각 테스트마다 한 번이라도 하위 등수를 받은 적이 있는지를 체크하기 위해 총 3중으로 구현했다. 사실
indexOf()메소드를 사용했기에 4중이라고 할 수 있다. - 멘토랑 멘티가 같은 사람일수는 없기에 그런 경우에는 continue로 바로 다음으로 넘어가게 했다.
LRU (카카오 캐시 문제 변형)
캐시메모리는 CPU와 주기억장치(DRAM) 사이의 고속의 임시 메모리로서 CPU가 처리할 작업을 저장해 놓았다가 필요할 바로 사용해서 처리속도를 높이는 장치이다.
워낙 비싸고 용량이 작아 효율적으로 사용해야 한다. 철수의 컴퓨터는 캐시메모리 사용 규칙이 LRU 알고리즘을 따른다. LRU 알고리즘은 Least Recently Used의 약자로 직역하자면 가장 최근에 사용되지 않은 것 정도의 의미를 가지고 있습니다.
캐시에서 작업을 제거할 때 가장 오랫동안 사용하지 않은 것을 제거하겠다는 알고리즘입니다.
만약 캐시의 사이즈가 5이고 작업이 2 3 1 6 7 순으로 저장되어있다면, (맨 앞이 가장 최근에 쓰인 작업이고, 맨 뒤는 가장 오랫동안 쓰이지 않은 작업이다.)
-
Cache Miss : 해야할 작업이 캐시에 없는 상태로 위 상태에서 만약 새로운 작업인 5번 작업을 CPU가 사용한다면 Cache miss가 되고 모든 작업이 뒤로 밀리고 5번작업은 캐시의 맨 앞에위치한다. 5 2 3 1 6 (7번작업은캐시에서삭제된다.)
-
Cache Hit : 해야할 작업이 캐시에 있는 상태로 위 상태에서 만약 3번 작업을 CPU가 사용한다면 Cache Hit가 되고, 63번 앞에 있는 5, 2번 작업은 한 칸 뒤로 밀리고, 3번이 맨 앞으로 위치하게 된다. 5 2 3 1 6 —> 3 5 2 1 6
캐시의 크기가 주어지고, 캐시가 비어있는 상태에서 N개의 작업을 CPU가 차례로 처리한다면 N개의 작업을 처리한 후 캐시메모리의 상태를 가장 최근 사용된 작업부터 차례대로 출력하는 프로그램을 작성하세요.
function solution(nums, s) {
cache = Array(s).fill(0);
for (let i = 0; i < nums.length; i++) {
if (cache.indexOf(nums[i]) === -1) {
cache.unshift(nums[i]);
cache.pop();
} else {
cache.splice(cache.indexOf(nums[i]), 1);
cache.unshift(nums[i]);
}
}
return cache;
}
console.log(solution([1, 2, 3, 2, 6, 2, 3, 5, 7], 5));
- 카카오 기출 문제라고 해서 약간 겁부터 냈는데, 생각보다 너무 빠르고 쉽게 풀었다.
- 스택과 관련된 문제라고 생각해서 우선 캐시라는 변수를 선언하고 그 안에 개수만큼 0을 삽입하였다.
- 이후 자바스크립트 내장 함수를 사용해서 문제를 해결하였다.
JS 강의 정리
spread
- 이 문법을 사용해서 객체 또는 배열을 펼칠 수 있다.
- 기존의 객체를 건드리지 않고, 그 객체가 가지고 있는 속성을 그대로 사용하여 새로운 객체를 만들 수 있다.
const slime = {
name: '슬라임'
};
const cuteSlime = {
...slime,
attribute: 'cute'
};
const purpleCuteSlime = {
...cuteSlime,
color: 'purple'
};
// 결괏값
{ name: '슬라임' }
{ name: '슬라임', attribute: 'cute' }
{ name: '슬라임', attribute: 'cute', color: 'purple' }
- spread 연산자는 배열에서도 사용할 수 있다.
const animals = ["개", "고양이", "참새"];
const anotherAnimals = [...animals, "비둘기"];
console.log(animals);
console.log(anotherAnimals);
// 결괏값
["개", "고양이", "참새"][("개", "고양이", "참새", "비둘기")];
- 기존의 animals를 건드리지 않으면서, 새로운 배열에 animals가 가지고 있는 내용을 모두 집어넣고, ‘비둘기’라는 항목을 추가적으로 넣었다.
- 배열에서 spread 연산자를 여러번 사용할 수도 있다.
rest
- rest는 객체, 배열, 그리고 함수의 파라미터에서 사용이 가능하다.
객체에서의 rest
const purpleCuteSlime = {
name: '슬라임',
attribute: 'cute',
color: 'purple'
};
const { color, ...rest } = purpleCuteSlime;
console.log(color);
console.log(rest);
// 결괏값
purple
{ name: '슬라임', attribute: 'cute' }
- rest 안에는 color 값을 제외한 값이 들어있다.
- rest는 객체와 배열에서는 사용할 때 비구조화 할당 문법과 함께 사용된다.
- 주로 사용할 때는 rest라는 키워드를 사용하게 되는데, 추출한 값의 이름이 꼭 rest일 필요는 없다.
배열에서의 rest
const numbers = [0, 1, 2, 3, 4, 5, 6];
const [one, ...rest] = numbers;
console.log(one);
console.log(rest);
// 결괏값
(0)[(1, 2, 3, 4, 5, 6)];
- 배열에서도 객체와 비슷하게 사용된다.
- 다만 주의할 점은 배열에서는 rest가 맨 마지막 요소에 위치해야 한다.
함수 파라미터에서의 rest
- rest를 함수 파라미터에서도 사용할 수 있다.
- 함수의 파라미터가 몇 개가 될지 모르는 상황에서 rest 파라미터를 사용하면 매우 유용하다.
function sum(...rest) {
return rest.reduce((acc, current) => acc + current, 0);
}
const result = sum(1, 2, 3, 4, 5, 6);
console.log(result); // 21
함수 인자와 spread
- 함수에서 값을 읽을 때, 그 값들은 파라미터라 부르고, 함수에서 값을 넣어줄 때, 그 값들은 인자라고 부른다.
- 함수 파라미터에서 rest를 사용한 것과 비슷한데, 반대의 역할을 한다.
function sum(...rest) {
return rest.reduce((acc, current) => acc + current, 0);
}
const numbers = [1, 2, 3, 4, 5, 6];
const result = sum(...numbers);
console.log(result);
자바스크립트 Scope
- Scope란, 변수나 함수를 선언하게 될 때 해당 변수 또는 함수가 유효한 범위를 의미한다.
- Scope는 총 3가지 종류가 있다.
- Global (전역) Scope: 코드의 모든 범위에서 사용이 가능
- Function (함수) Scope: 함수 안에서만 사용이 가능
- Block (블록) Scope: if, for, switch 등 특정 블록 내부에서만 사용이 가능
const와let로 선언한 값은 Block Scope로 선언이 되기 때문에 if문 같은 블록 내에서 새로운 변수/상수를 선언하게 된다면, 해당 블록 내부에서만 사용이 가능하고, 블록 밖의 범위에서 똑같은 이름을 가진 값이 있다고 해도 영향을 주지 않는다.- 반면, var는 Function Scope로 선언이 되므로, if문 블록 내부에서 선언한 value 값이 블록 밖의 value에도 영향을 미치게 된다.
Hoisting
- 자바스크립트에서 아직 선언되지 않은 함수/변수를 끌어올려서 사용할 수 있는 자바스크립트의 작동 방식을 의미한다.
myFunction();
function myFunction() {
console.log("hello world!");
}
- 위의 코드를 보면 myFunction함수를 선언하기 전에 호출했음에도 불구하고 코드는 정상적으로 작동한다.
- 잘 작동하는 이유는, 자바스크립트 엔진이 위 코드를 해석하는 과정에서 Hoisting하여 받아들이게 되기 때문이다.
- 변수 또한 Hoisting된다.
console.log(number);
var number = 2;
// 결괏값
undefined;
- 원래는 number이 정의되지 않았다는 에러메세지가 발생해야 하는데, 자바스크립트 엔진이 위 코드를 해석할 때 다음과 같이 받아들이게 되기 때문에 undefined가 출력된다.
var number;
console.log(number);
number = 2;
- 반면,
const와let은 호이스팅은 되지만 변수 생성과정이 달라서 TDZ가 생성되어 초기화전에는 액세스할 수 없다는 에러가 발생하게 된다. - Hoisting은 자바스크립트 엔진이 갖고 있는 성질이며, 방지하는 것이 좋다.
- Hoisting이 발생하는 코드는 이해하기 어렵고 유지보수도 힘들어지고 의도치 않는 결과물이 나타나기 쉽기 때문
- 함수의 경우 꼭 선언 후에 호출을 하자.
var대신const,let을 위주로 사용하자
0723
10일 차 알고리즘 문제 풀이
알고리즘
K번째 큰 수
현수는 1부터 100사이의 자연수가 적힌 N장의 카드를 가지고 있습니다. 같은 숫자의 카드가 여러장 있을 수 있습니다.
현수는 이 중 3장을 뽑아 각 카드에 적힌 수를 합한 값을 기록하려 고 합니다. 3장을 뽑을 수 있는 모든 경우를 기록합니다.
기록한 값 중 K번째로 큰 수를 출력 하는 프로그램을 작성하세요.
만약 큰 수부터 만들어진 수가 25 25 23 23 22 20 19……이고 K값이 3이라면 K번째 큰 값 은 22입니다.
function solution(nums, k) {
let answer = 0;
let arr = [];
for (let i = 0; i < nums.length - 2; i++) {
for (let j = i + 1; j < nums.length - 1; j++) {
for (let k = j + 1; k < nums.length; k++) {
let sum = nums[i] + nums[j] + nums[k];
if (!arr.includes(sum)) {
arr.push(sum);
}
}
}
}
arr.sort((a, b) => b - a);
answer = arr[k - 1] || -1;
return answer;
}
console.log(solution([13, 15, 34, 23, 45, 65, 33, 11, 26, 42], 3));
console.log(solution([5, 5, 5, 4, 4, 3, 2, 1], 2));
- 세 장을 뽑아야 하기 때문에 3중 반복문을 실행했다.
- 반복문의 범위를 첫 번째는 0부터 배열의 길이 - 2까지, 두 번째는 i + 1부터 배열의 길이 - 1까지, 마지막은 j + 1부터 배열의 길이까지 돌게 해서, 동일한 연산을 또 하거나 같은 카드를 더하는 일이 없도록 했다.
- 세 카드를 합한 값을 새로운 배열에 없는 값이라면 그 배열에 삽입했다.
- 그리고 배열을 내림차순으로 정렬해서 k번째 값이 있다면 그 값을, 없다면 -1을 반환하도록 했다.
- 내림차순:
arr.sort((a, b) => b - a);
- 내림차순:
졸업 선물
선생님은 올해 졸업하는 반 학생들에게 졸업선물을 주려고 합니다.
학생들에게 인터넷 쇼핑몰에서 각자 원하는 상품을 골라 그 상품의 가격과 배송비를 제출하라 고 했습니다. 선생님이 가지고 있는 예산은 한정되어 있습니다.
현재 예산으로 최대 몇 명의 학생에게 선물을 사줄 수 있는지 구하는 프로그램을 작성하세요. 선생님은 상품 하나를 50% 할인해서(반 가격) 살 수 있는 쿠폰을 가지고 있습니다. 배송비는 할인에 포함되지 않습니다.
function solution(nums, m) {
let answer = 0;
let price = 0;
let discount = 0;
nums.sort((a, b) => a[0] + a[1] - (b[0] + b[1]));
console.log(nums);
for (let j = 0; j < nums.length; j++) {
price = nums[j][0] + nums[j][1];
discount = nums[j][0] / 2 + nums[j][1] / 2;
if (m > price) {
m -= price;
answer++;
} else if (m > discount) {
m -= discount;
answer++;
} else {
break;
}
}
return answer;
}
console.log(
solution(
[
[6, 6],
[2, 2],
[4, 3],
[4, 5],
[10, 3],
],
28
)
);
console.log(
solution(
[
[8, 6],
[2, 2],
[4, 3],
[4, 5],
[6, 4],
],
27
)
);
console.log(
solution(
[
[8, 6],
[2, 2],
[4, 3],
[4, 5],
[12, 1],
],
41
)
);
- 사실 이 문제는 어떻게 답을 도출해내기는 했지만 이 답이 확실한지 장담을 못하겠다.
- 우선 물건값과 배송비를 더한 결과로 내림차순 정렬을 했다.
- 그리고 반복문을 실행해서 일반 가격과 할인 가격을 모두 구하고, if문으로 현재 가진 돈이 가격보다 크다면 금액을 빼고 답을 1 증가시켰고, 현재 가진 돈이 일반 가격보단 작지만 할인 가격보단 크다면 그 금액으로 구매했다. 그리고 둘 다 충족하지 않는다면 반복문을 굳이 더 실행할 이유가 없기에 종료시켰다.
- 논리적으로만 생각해서 코드를 이렇게 구현했지만 과연 이게 모든 테스트 케이스를 통과할 수 있을까하는 의문이 들었다.
최대 매출
현수의 아빠는 제과점을 운영합니다. 현수 아빠는 현수에게 N일 동안의 매출기록을 주고 연속 된 K일 동안의 최대 매출액이 얼마인지 구하라고 했습니다.
만약 N=10이고 10일 간의 매출기록이 아래와 같습니다. 이때 K=3이면
12 15 11 20 25 10 20 19 13 15
연속된 3일간의 최대 매출액은 11+20+25=56만원입니다. 여러분이 현수를 도와주세요.
function solution(nums, k) {
let answer = 0;
for (let i = 0; i < nums.length - k + 1; i++) {
let sum = 0;
for (let j = i; j < i + k; j++) {
sum += nums[j];
}
console.log(sum);
if (sum > answer) answer = sum;
}
return answer;
}
console.log(solution([12, 15, 11, 20, 25, 10, 20, 19, 13, 15], 3));
- 연속 매출을 기준으로 값을 구해야 하기에 반복문의 범위 설정이 중요했다.
- 첫 번째 반복문은 받은 매출기록 길이만큼 반복하고, 두 번째 반복문에서 변수를 i부터 i + k까지 실행시켜서 연속된 k일의 매출을 구하도록 했다.
- 그리고 최대값을 구할 수 있도록 비교하는 코드를 작성해 결과를 도출했다.
공통원소 구하기
A, B 두 개의 집합이 주어지면 두 집합의 공통 원소를 추출하여 오름차순으로 출력하는 프로그램을 작성하세요. 시간복잡도를 고려해야 하는 문제입니다.
function solution(numA, numB) {
let answer = [];
for (let i = 0; i < numA.length; i++) {
for (let j = 0; j < numB.length; j++) {
if (numA[i] === numB[j]) answer.push(numA[i]);
}
}
answer.sort((a, b) => a - b);
return answer;
}
console.log(solution([1, 3, 9, 5, 2], [3, 2, 5, 7, 10]));
- 이중 반복문을 이용해서 쉽게 구현하였지만, 문제에 시간복잡도를 고려해야 하는 문제라고 쓰여있는 것을 보아서는 정답은 아니라고 생각한다.
- 하지만 아직 특정 알고리즘까지 학습하지는 않아서 어떤 것을 적용해야 할지는 모르겠다. 아직은 사전 과제이기 때문에 일단은 이렇게 해결하고 알고리즘을 학습한 뒤, 그 알고리즘을 적용하여 시간복잡도를 만족시켜야겠다.
연속 부분수열
N개의 수로 이루어진 수열이 주어집니다.
이 수열에서 연속부분수열의 합이 특정숫자 M이 되는 경우가 몇 번 있는지 구하는 프로그램을 작성하세요.
만약 N=8, M=6이고 수열이 다음과 같다면
12131112
합이 6이 되는 연속부분수열은 {2, 1, 3}, {1, 3, 1, 1}, {3, 1, 1, 1}로 총 3가지입니다. 시간복잡도를 고려해야 하는 문제입니다.
function solution(nums, m) {
answer = 0;
for (let i = 0; i < nums.length; i++) {
let sum = nums[i];
if (sum === m) {
answer++;
continue;
}
for (let j = i + 1; j < nums.length; j++) {
sum += nums[j];
if (sum === m) {
answer++;
break;
} else if (sum > m) {
break;
}
}
}
return answer;
}
console.log(solution([1, 2, 1, 3, 1, 1, 1, 2], 6));
- 이 문제도 시간복잡도를 고려해야 하는 문제였다. 비록 이중반복문을 사용해서 구현하기는 했지만, 조금이라도 반복 횟수를 줄이기 위해서 코드를 고민했다.
- 우선 수열의 첫 번째 값이 특정 숫자와 같다면 바로 반복문을 건너띄고 다음 인덱스로 넘어갔다.
- 그리고 순차적으로 값을 더해서 값이 특정 숫자의 값을 넘어가면 바로 반복문을 종료시키고 다음 인덱스로 넘어가게 구현해서 쓸데없이 반복이 되지 않도록 구현했다.
올바른 괄호
괄호가 입력되면 올바른 괄호이면 “YES”, 올바르지 않으면 ”NO”를 출력합니다.
(())() 이것은 괄호의 쌍이 올바르게 위치하는 거지만, (()()))은 올바른 괄호가 아니다.
function solution(s) {
let answer = "";
let leftCnt = 0;
let rightCnt = 0;
for (let i = 0; i < s.length; i++) {
if (s.charAt(i) === "(") leftCnt++;
else if (s.charAt(i) === ")") rightCnt++;
}
answer = leftCnt === rightCnt ? "YES" : "NO";
return answer;
}
console.log(solution("(()(()))(()"));
- 문자열을 반복문으로 돌면서 각 문자를 체크해서 괄호 개수를 세고, 양 개수가 같다면 YES 아니라면 NO를 반환하였다.
공주구하기
정보 왕국의 이웃 나라 외동딸 공주가 숲속의 괴물에게 잡혀갔습니다.
정보 왕국에는 왕자가 N명이 있는데 서로 공주를 구하러 가겠다고 합니다. 정보왕국의 왕은 다음과 같은 방법으로 공주를 구하러 갈 왕자를 결정하기로 했습니다.
왕은 왕자들을 나이 순으로 1번부터 N번까지 차례로 번호를 매긴다.
그리고 1번 왕자부터 N 번 왕자까지 순서대로 시계 방향으로 돌아가며 동그랗게 앉게 한다. 그리고 1번 왕자부터 시 계방향으로 돌아가며 1부터 시작하여 번호를 외치게 한다.
한 왕자가 K(특정숫자)를 외치면 그 왕자는 공주를 구하러 가는데서 제외되고 원 밖으로 나오게 된다. 그리고 다음 왕자부터 다시 1부터 시작하여 번호를 외친다.
이렇게 해서 마지막까지 남은 왕자가 공주를 구하러 갈 수 있다.
예를 들어 총 8명의 왕자가 있고, 3을 외친 왕자가 제외된다고 하자. 처음에는 3번 왕자가 3 을 외쳐 제외된다.
이어 6, 1, 5, 2, 8, 4번 왕자가 차례대로 제외되고 마지막까지 남게 된 7번 왕자에게 공주를 구하러갑니다.
N과 K가 주어질 때 공주를 구하러 갈 왕자의 번호를 출력하는 프로그램을 작성하시오.
function solution(n, k) {
let answer = 0;
let arr = new Array(n);
for (let i = 0; i < n; i++) {
arr[i] = i + 1;
}
for (let i = 0; i < n - 1; i++) {
for (let i = 0; i < k; i++) {
let item = arr.shift();
arr.push(item);
}
arr.pop();
}
answer = arr[0];
return answer;
}
console.log(solution(8, 3));
- 새로운 n개의 요소를 가진 배열을 선언하고, 배열에 1부터 n까지 값을 초기화했다.
- 배열을 돌면서, 배열 내장함수를 사용하여 요구사항을 만족시키고 최종 남은 값을 출력했다.
0724
Git 수업 내용 정리
gitignore
- 이런 종류의 파일들은 트래킹할 필요 없다고 깃에게 알려준다.
trailing comma
- 묶음 자료형 마지막 아이템 뒤에 ,를 붙이는 것
- 아이템을 한 줄씩 작성하고 이 방법을 적용하면 깃에서 추가된 아이템을 한 눈에 확인할 수 있다.
Branch
- 분기점을 생성하여 독립적으로 코드를 변경할 수 있도록 도와주는 모델
branch 확인 명령어
git branch: 사용 가능한 로컬 브랜치 표시git branch -r: 사용 가능한 원격 브랜치 표시git branch -a: 사용 가능한 모든(로컬 + 원격) 브랜치 표시
branch 생성, 이동, 병합, 삭제 명렁어
git branch 브랜치명: 브랜치 생성git switch 브랜치명: 해당 브랜치로 이동. 최신 명령어- 이전에는
git checkout 브랜치명명령어를 사용
- 이전에는
git merge 브랜치명: 해당 브랜치를 병합.- main 브랜치에서 자식 브랜치를 병합하고 싶다면, main 브랜치로 이동해서 병합할 자식 브랜치를 선택
git branch -d stem: 로컬 브랜치 삭제. 로컬에서만 삭제되고 원격 브랜치는 살아있다.-D옵션을 주면 충돌이 해결되지 않거나 정상적으로 머지되어 있지 않아서 삭제가 불가능한 브랜치를 강제로 삭제해준다.
branching models
git flow
- (hotfix) - main - (release) - develop - feature
- 가장 많이 적용, 각 단계가 명확히 구분. 복잡
- git-flow는 병합(merge) 기반의 솔루션. feature 브랜치를 리베이스 하지 않는다.
설치 (mac os)
brew install git-flow-avh
초기화
git flow init- 기존 git 저장소 내에서 초기화하는 것으로 git-flow의 사용을 시작
- 기본 값을 사용하기를 권장
새 기능 시작
git flow feature start 기능이름- 새 기능의 개발은 ‘develop’ 브랜치에서 시작한다.
- ‘develop’에 기반한 새 기능 브랜치를 생성하고, 그 브랜치로 전환
기능 완료
git flow feature finish 기능이름- 기능이름 브랜치를 ‘develop’에 병합(merge)한다.
- 기능 브랜치를 삭제한다.
- ‘develop’ 브랜치로 전환한다.
릴리스 시작
git flow release start 릴리스명- ‘develop’ 브랜치로부터 ‘release’ 브랜치를 생성한다.
릴리즈 완료
git flow release finish 릴리스명- git 브랜칭 중에서 가장 큰 단계
- ‘release’ 브랜치를 ‘main’ 브랜치에 병합(merge)
- 릴리스를 릴리스 이름으로 태그
- 릴리스를 ‘develop’ 브랜치로 재병합
- ‘release’ 브랜치 삭제
- 마지막으로
git push --tags를 사용해 태그들을 push 해야 한다. - develop과 main 브랜치를 각각 remote에 push해서 원격저장소에 적용해줘야 한다.
0725
되돌리기 관련 명령어
Rename
- Worst
mv server.py main.py- 파일이 지워지고, 새로 파일이 생성된다. 의도에 맞지 않음
- Best
git mv server.py main.py- 의도에 맞게 파일을 rename하고 바로 커밋한다.
Undoing
git checkout -- .orgit checkout -- {filename}- 최신 깃에서는
git restore -- {filename}
Unstaging
- 스테이지에서 다시 워킹 디렉토리로 보낸다.
git reset HEAD {filename}
Edit lastest commit
git commit --amend- 가장 최신 커밋 메시지를 수정한다.
Reset Commit
- 협업 시, push 후에 절대로 사용하지 말 것
- Worst case: Reset
git reset --hard HEAD~3git push -f origin <branch>- HEAD부터 세개의 커밋을 되돌린다는 뜻
- 잘못한 이력도 commit으로 박제하고 수정한 이력을 남기자
- Best case: Revert
git revert --no-commit HEAD~3git commitgit push origin <branch>--no commit옵션은 원래 revert를 하나씩 해야하는데 n개를 한 번에 가능하게 해준다.- 잘못하기 전 과거로 돌아가 최신을 유지하면서 되돌렸다는 이력을 commit으로 남겨 모든 팀원이 이 사항을 공유할 수 있다.
협업
Collaboration
- 어떤 리포지토리에 협업을 하고싶은 사람이 있다면, 콜라보레이터로 추가해서 같이 작업을 할 수 있다.
- 초대받은 사람이 자유롭게 push할 수 있지만, 협업할 때는 적절하지 않다.
- 코드리뷰를 받고 싶거나, 이슈나 프로젝트 관리 등 본 코드와 관련 없는 일을 하는 사람한테만 초대한다. (충돌이 많이 발생하는 문제가 있기 때문)
Fork and Merge
팀장이 할 일
- 팀장이 repository를 github에서 생성한다.
- 팀장은 본인의 repo 주소를 복사하여 clone한다.
- develop을 만든 뒤에 다른 사람들이 가져가야 하므로,
git flow init를 사용해 develop 브랜치를 만든다.- 실제 일을 하는 사람들은 develop이라는 브랜치를 사용하기 때문에
- develop을 처음 쓰는 거기 때문에
git push -u origin develop명령어를 사용해서 푸시를 해준다. - 팀장은 본인 repo에 issue가 발생하면 답글, 할당, 라벨 등을 해준다.
- project를 생성하여 issue를 관리한다.
- 팀원이 pull request를 보내면 메일이 오는데, 클릭하면 파일 변화를 확인할 수 있다.
- 변화 파일에서 코드를 클릭하여 코멘트를 남길 수 있다.
- Finish your review에서 request changes를 선택하고 리뷰를 보내면 댓글이 달린다.
- 작업이 확인되면 approve 처리를 하고, merge pull request 버튼을 글릭하고 confirm 버튼까지 클릭하면 팀장의 remote repo에 작업 결과가 반영된다.
- remote repo에 반영된 작업을 로컬에서도 받아와야 하므로,
git fetch origin develop명령어로 패치를 받는다. - 패치가 FETCH_HEAD로 임시 브랜치로 쌓이게 되는데, 이 FETCH_HEAD를 develop에 merge한다.
git merge FETCH_HEAD
팀원이 할 일
- 팀장이 만든 repo 주소로 들어가서 Fork를 한다.
- fork한 레포는 팀원의 소유가 되기 때문에 자유롭게 올릴 수 있다.
- 작업물을 본 코드에 적용해달라고 pull request를 할 수 있다.
- 본인의 repo를 clone한다.
- 팀원도
git flow init을 한다.- 각자 다른 로컬 저장소기 때문에, 각자 해야한다.
- 팀장의 repo에 방문하여 issue를 작성한다.
- 이슈 확인을 받고,
git flow feature start 기능이름으로 작업을 진행한다. - 작업을 마치고
git flow feature finish 기능이름으로 작업을 마무리한다. - 팀원의 develop에서 팀장의 develop으로 넘겨야 하므로,
git push -u origin develop으로 우선 remote repo로 보낸다. - 팀원의 repo를 확인하면 push 받은 내역을 인지하여, Compare&pull request 버튼이 활성화된다.
- 팀원의 develop -> 팀장의 develop으로 설정하고 제목과 내용을 작성하여 pull request를 생성한다.
- 내용에는 해당 pull request가 어떤 이슈에 대한 해결인지
resolved: #번호를 이용해 이슈 번호를 작성한다.
- 내용에는 해당 pull request가 어떤 이슈에 대한 해결인지
- 팀장에게 수정사항을 받으면 해당 작업을 진행한다.
- pull request가 열려있는 상태에서는 팀원의 develop에서 push가 발생하면 github에서는 지금 열려있는 pull request에 대한 보수공사로 판단한다.
- 그래서 pull request 밑에 자동으로 붙는다.
- develop에서 바로 작업을 진행하고 push를 해서 작업을 마무리한다.
- 나머지 팀원들도 패치하고 병합한다. upstream이 설정되어 있지 않은 경우, 설정해줘야 한다.
git remote add upstream {팀장저장소주소}git fetch upstream developgit merge FETCH_HEADgit push- 팀원은 팀장과 다르게 upstream 저장소를 패치해야 한다.