코테 9/21

Rami·2024년 9월 21일

TodayILearn

목록 보기
10/66

배열 만들기 2

정수 l과 r이 주어졌을 때, l 이상 r이하의 정수 중에서 숫자 "0"과 "5"로만 이루어진 모든 정수를 오름차순으로 저장한 배열을 return 하는 solution 함수를 완성해 주세요.

만약 그러한 정수가 없다면, -1이 담긴 배열을 return 합니다.

제한사항
1 ≤ l ≤ r ≤ 1,000,000
입출력 예
l r result
5 555 [5, 50, 55, 500, 505, 550, 555]
10 20 [-1]
입출력 예 설명
입출력 예 #1

5 이상 555 이하의 0과 5로만 이루어진 정수는 작은 수부터 5, 50, 55, 500, 505, 550, 555가 있습니다. 따라서 [5, 50, 55, 500, 505, 550, 555]를 return 합니다.
입출력 예 #2

10 이상 20 이하이면서 0과 5로만 이루어진 정수는 없습니다. 따라서 [-1]을 return 합니다.


내가 푼것

function solution(l, r) {
    let answer = "";
    let regExp = /^[05]+$/;
    for(let i = l; i <= r; i++){
        if(regExp.test(i)){
            answer += i;
        } else {
            answer = -1
        }
    }
} return answer;


질문과 답변

Q1: >=는 어떻게 사용하나요?

for 루프의 조건에서 >= 연산자를 잘못 사용한 것 같아요. for 루프에서 사용하는 일반적인 구조는 다음과 같습니다:

for (let i = l; i <= r; i++) {
    // 반복할 코드
}

변수 선언 부분에서는 >= 같은 조건 연산자를 사용할 수 없고, 조건문에서만 사용할 수 있습니다. 예를 들어 i <= r처럼 조건을 설정해야 합니다.


Q2: 정규 표현식 "^[05]+$"의 의미는 무엇인가요?

"^[05]+$"정규 표현식으로, 특정 문자열이 오직 '0'과 '5'로만 이루어졌는지 검사할 수 있습니다.

  • ^: 문자열의 시작을 의미합니다.
  • [05]: '0' 또는 '5'를 의미합니다.
  • +: 앞의 패턴이 한 번 이상 반복됨을 의미합니다.
  • $: 문자열의 을 의미합니다.

이 정규 표현식은 문자열이 오직 '0'과 '5'로만 이루어져 있을 때 true를 반환합니다.


Q3: test() 메서드는 무엇인가요?

test()는 정규 표현식을 사용하여 문자열이 특정 패턴과 일치하는지 확인하는 JavaScript 메서드입니다. 일치하면 true, 그렇지 않으면 false를 반환합니다.

let regExp = /^[05]+$/;
let num = "505";

console.log(regExp.test(num));  // true

위 코드는 숫자 505가 문자열 "505"로 변환된 후, 정규 표현식과 일치하는지 검사하여 true를 반환합니다.


Q4: i.toString()은 왜 필요한가요?

정규 표현식은 문자열을 검사하는 것이기 때문에, 숫자를 문자열로 변환할 필요가 있습니다. 숫자 ii.toString()을 사용하여 문자열로 변환한 후 정규 표현식으로 검사해야 합니다.

예시:

if (regExp.test(i.toString())) {
    answer.push(i);
}

여기서 i.toString()은 숫자 i를 문자열로 변환하는 함수입니다. 정규 표현식은 이 변환된 문자열이 오직 '0'과 '5'로 이루어져 있는지 검사합니다.


Q5: 왜 else에서 -1을 바로 반환하면 안 되나요?

else 조건에서 바로 -1을 배열에 추가하면, 조건을 만족하지 않는 숫자들마다 -1이 배열에 추가됩니다. 그러면 -1이 여러 개 배열에 들어갈 수 있어요.

따라서 모든 숫자를 검사한 후에, 배열이 비어 있을 때만 [-1]을 반환하는 방식으로 처리해야 합니다.


Q6: -1[-1]의 차이는 무엇인가요?

  • -1: 숫자형 데이터로, 단순히 숫자 -1을 반환합니다.
  • [-1]: 배열 데이터로, 배열 안에 숫자 -1이 들어 있는 형태입니다.

문제에서는 [-1]을 배열로 반환하라고 요구하고 있기 때문에, -1이 아닌 [-1]로 반환해야 합니다.


최종 코드

function solution(l, r) {
    let answer = [];
    let regExp = /^[05]+$/;

    for (let i = l; i <= r; i++) {
        if (regExp.test(i.toString())) {
            answer.push(i);  // 조건을 만족하는 숫자만 배열에 추가
        } 
    }

    // 배열이 비어 있으면 [-1]을 반환
    if (answer.length === 0) {
        return [-1];
    }

    return answer;
}

코드 설명:

  1. 정규 표현식 검사: i를 문자열로 변환한 후 정규 표현식으로 검사하여 '0'과 '5'로만 이루어진 숫자인지 확인합니다.
  2. 숫자 추가: 조건을 만족하는 숫자들만 배열에 추가합니다.
  3. 결과 반환: 배열이 비어있다면 [-1]을 반환하고, 그렇지 않으면 배열을 반환합니다.


다른풀이들

1. 코드 1: Generator를 사용한 방식

function* gen50() {
    let i = 1;

    while (true) {
        yield Number(Number(i).toString(2)) * 5;
        i++;
    }
}

function solution(l, r) {
    const n = gen50();
    let a = 0;
    const arr = [];

    while (a < l) {
        a = n.next().value; 
    }
    while (a <= r) {
        arr.push(a); 
        a = n.next().value; 
    }

    return arr.length ? arr : [-1];
}

코드 설명:

  • gen50(): 이 함수는 Generator 함수로, 호출할 때마다 i 값을 2진수로 변환하여 5와 곱한 값을 반환합니다. 이를 통해 '0'과 '5'로만 이루어진 값을 무한히 생성할 수 있습니다.
    • i.toString(2)로 숫자 i2진수 문자열로 변환한 후, 다시 숫자로 변환(Number())하고 마지막에 * 5로 해당 값을 5로 곱합니다.
    • while(true) 루프는 무한히 반복하여 값을 생성하고 yield로 값을 내보냅니다.
  • solution(l, r):
    1. gen50()으로 생성된 값을 변수 n에 저장하고 n.next().value로 하나씩 값을 가져옵니다.
    2. l보다 작은 값은 스킵하고(a = n.next().value), l부터 r까지의 값만 배열에 넣습니다.
    3. 범위가 끝날 때까지 값들을 배열에 채운 후, 배열이 비어 있으면 [-1]을 반환합니다.

사용된 메서드 설명:

  • yield: Generator 함수에서 중간에 값을 반환하고, 이후 다시 호출되었을 때 그 자리에서 실행을 계속할 수 있게 하는 기능입니다.
    • 사용 예시:
      function* exampleGen() {
          yield 1;
          yield 2;
          yield 3;
      }
      const gen = exampleGen();
      console.log(gen.next().value); // 1
      console.log(gen.next().value); // 2
  • toString(2): 숫자를 2진수로 변환합니다. toString(2)를 사용하면 숫자를 2진수 문자열로 변환할 수 있습니다.
    • 사용 예시:
      console.log((5).toString(2)); // "101"
  • n.next().value: Generator 함수의 다음 값을 가져옵니다. n.next()는 객체로 { value: 값, done: false/true }를 반환하고, 그중 value만 가져옵니다.
    • 사용 예시:
      const n = exampleGen();
      console.log(n.next().value); // 1

장점:

  • 효율적인 수 생성: 필요한 값만 계산하고, 메모리를 절약할 수 있습니다.
  • 유연성: gen50은 무한히 값을 생성할 수 있어, 어디서든 원하는 만큼의 값을 가져올 수 있습니다.

단점:

  • 이해하기 어려움: Generator와 yield는 초보자에게는 낯설고 이해하기 어렵습니다.
  • 복잡도: 문제를 해결하는 방식이 다소 복잡하고, 이진수 변환과 5배를 곱하는 방식은 직관적이지 않을 수 있습니다.

보완점:

  • Generator의 복잡성을 줄이고 더 직관적인 방법으로 문제를 해결할 수 있습니다.

2. 코드 2: Array.from()filter()를 사용한 방식

function solution(l, r) {
    const result = Array.from({ length: r - l + 1 }, (_, i) => i + l)
        .filter(n => !/[^05]/.test(n));
    return result.length ? result : [-1];
}

코드 설명:

  1. Array.from(): l부터 r까지의 범위에 해당하는 배열을 생성합니다.

    • { length: r - l + 1 }l부터 r까지의 길이만큼의 배열을 생성한 후, 두 번째 인수로 (_, i) => i + l을 통해 l에서 시작하는 숫자 배열을 만듭니다.
  2. filter(): Array.from()으로 생성된 배열에서 각 숫자를 문자열로 변환하고, 정규 표현식 [^05]을 사용해 '0'과 '5' 이외의 숫자가 포함된 경우를 걸러냅니다.

    • 정규식 /[^05]/는 '0'과 '5' 이외의 문자가 포함된 경우 true를 반환하므로, filter()는 이를 제외합니다.
  3. 결과 반환: 필터링된 배열이 비어 있으면 [-1]을 반환하고, 그렇지 않으면 결과 배열을 반환합니다.

사용된 메서드 설명:

- Array.from()

Array.from()유사 배열 객체 또는 이터러블 객체를 배열로 변환하는 메서드입니다. 두 가지 인수를 받을 수 있는데:
- 첫 번째 인수: 배열로 변환할 유사 배열 객체 또는 이터러블 객체
- 두 번째 인수(선택): 각 요소에 대해 실행할 함수(맵핑 함수)

Array.from(arrayLike, mapFn)

현재 코드에서 Array.from()의 구조

Array.from({ length: r - l + 1 }, (_, i) => i + l)
  • 첫 번째 인수: { length: r - l + 1 }

    • 이 부분은 배열의 길이를 정의하는 객체입니다. 여기서 r - l + 1l에서 r까지의 숫자를 포함한 범위의 길이입니다.
    • 예를 들어, l = 5이고 r = 10이라면, r - l + 16이므로, length: 6인 배열을 생성하게 됩니다. 이 배열은 [undefined, undefined, undefined, ...] 형태로 채워진 임시 배열입니다.
  • 두 번째 인수: (_, i) => i + l

    • 이 부분은 맵핑 함수로, 첫 번째 인수는 현재 배열 요소의 값을 의미하는데, 여기서는 생략되어 (_)로 표시되어 있습니다(이 값은 사용할 필요가 없으므로 생략한 것입니다).
    • 두 번째 인수인 i현재 배열 요소의 인덱스입니다.

    이 함수는 각 요소의 인덱스(i)에 l을 더한 값을 배열의 요소로 변환합니다. 즉, 배열 [0, 1, 2, 3, ...][l, l+1, l+2, ...]처럼 변환됩니다.

예시:

만약 l = 5이고, r = 10이라면:

Array.from({ length: r - l + 1 }, (_, i) => i + l)

는 다음과 같은 배열을 생성합니다:

Array.from({ length: 6 }, (_, i) => i + 5)
// [5, 6, 7, 8, 9, 10]

전체 흐름

  • Array.from({ length: r - l + 1 }): l에서 r까지의 숫자를 포함하는 배열을 생성합니다.
    • 예: l = 5, r = 10이면 [5, 6, 7, 8, 9, 10]이라는 배열을 생성.
  • filter(n => !/[^05]/.test(n)): 각 숫자를 문자열로 변환한 뒤, 정규 표현식 /[^05]/로 '0'과 '5' 이외의 숫자가 있는지 검사합니다. '0'과 '5' 이외의 숫자가 없으면 그 숫자를 결과 배열에 남기고, 있으면 제외합니다.

맵핑 함수 설명: (_, i) => i + l

  • _: 첫 번째 인수로 배열 요소 값이 들어오지만, 이 코드는 배열 요소 값에 관심이 없기 때문에 무시하고 _로 표시합니다.
  • i: 배열의 인덱스를 가리킵니다.
  • i + l: 인덱스 il을 더한 값을 반환합니다. 이는 l부터 시작하는 숫자들을 만들기 위한 계산입니다.

다시 예시로:

l = 5, r = 10일 때:

  • i = 00 + 5 = 5
  • i = 11 + 5 = 6
  • i = 22 + 5 = 7
  • ...
  • i = 55 + 5 = 10

최종적으로 [5, 6, 7, 8, 9, 10]이라는 배열이 만들어집니다.

- filter()

배열을 순회하면서 조건을 만족하는 요소들로만 새로운 배열을 생성합니다.

  • 사용 예시:

    const arr = [1, 2, 3, 4];
    const evenNumbers = arr.filter(n => n % 2 === 0); // [2, 4]
  • 정규 표현식 [^05]: 이 정규식은 '0'과 '5'를 제외한 문자가 포함된 경우 true를 반환합니다. filter()에서 이를 제외하여 '0'과 '5'로만 구성된 숫자들을 남깁니다.

    • 사용 예시:
      const test = /[^05]/.test("507"); // true ('7'이 포함되어 있음)

장점:

  • 간결함: Array.from()filter()를 사용해 코드를 매우 간결하게 작성했습니다.
  • 가독성: 코드가 한눈에 들어오며, 숫자를 문자열로 변환하고 필터링하는 과정이 직관적입니다.

단점:

  • 메모리 사용: Array.from()으로 먼저 전체 배열을 생성한 후, 다시 filter()로 필터링하므로 메모리 사용이 비효율적일 수 있습니다.
  • 비효율성: 범위가 커지면 불필요한 메모리와 계산을 많이 사용하게 됩니다.

보완점:

  • Array.from()으로 전체 배열을 생성하는 대신, 필요한 숫자만 계산하도록 코드를 수정하면 성능을 개선할 수 있습니다.


2번코드 추가 설명
Array.from()을 사용한 이유는 코드의 간결함함수형 프로그래밍 스타일을 활용하기 위해서입니다.

일반적으로 for 루프와 같은 반복문을 사용하는 방식보다 Array.from()과 같은 함수형 메서드를 사용하면 다음과 같은 장점이 있습니다:

1. 간결함:

  • for 루프를 사용하는 경우는 코드가 좀 더 길어지고, 초기화, 조건, 증감 등을 직접 처리해야 합니다. 반면, Array.from()을 사용하면 한 줄로 배열을 생성하고 각 요소를 쉽게 처리할 수 있습니다.

for 루프 예시:

function solution(l, r) {
    let result = [];
    for (let i = l; i <= r; i++) {
        result.push(i);
    }
    return result;
}

위의 for 루프는 더 길게 작성되어야 하고, 배열에 push()해야 하는 추가 작업이 필요합니다.

Array.from() 사용:

const result = Array.from({ length: r - l + 1 }, (_, i) => i + l);

이 코드는 한 줄로 배열 생성과 값을 배열에 넣는 작업을 동시에 해결할 수 있습니다. Array.from()을 사용하면 코드가 훨씬 간결해지고 읽기 쉬워집니다.

2. 함수형 프로그래밍 스타일:

  • 함수형 프로그래밍은 상태를 변경하지 않고, 순수 함수와 메서드를 사용하는 방식으로 코드를 작성하는 프로그래밍 스타일입니다. Array.from()은 함수형 프로그래밍의 장점인 가독성유연성을 극대화할 수 있습니다.
  • Array.from()과 같은 함수형 메서드는 쉽게 체이닝(연결)할 수 있고, 메서드 체이닝으로 다른 배열 메서드(filter, map, reduce)와도 쉽게 결합할 수 있습니다.

예를 들어, Array.from()으로 배열을 생성한 후에 바로 filter() 메서드를 적용하여 숫자를 걸러내는 작업을 쉽게 할 수 있습니다. 이 방식은 배열 조작과 필터링을 간결하게 한 줄로 처리할 수 있게 해줍니다.

const result = Array.from({ length: r - l + 1 }, (_, i) => i + l)
    .filter(n => !/[^05]/.test(n));

3. 가독성:

  • 함수형 메서드를 사용하면 목적이 명확한 코드를 작성할 수 있습니다. Array.from()을 사용하면 이 코드를 본 사람은 배열을 생성하는 코드라는 걸 쉽게 알 수 있고, 그 다음에 이어지는 filter()필터링 조건도 명확하게 이해할 수 있습니다.
  • for 루프는 배열 생성과 값을 추가하는 과정이 나눠져 있어, 목적을 파악하는 데 약간 더 시간이 걸릴 수 있습니다.

4. 유연성:

  • Array.from()을 사용하면 배열의 크기와 각 요소를 정의하는 로직을 쉽게 확장할 수 있습니다. 예를 들어, 지금은 i + ll부터 r까지 숫자를 생성하지만, 필요한 경우에는 더 복잡한 로직으로 각 요소를 정의할 수도 있습니다.

요약:

  • Array.from()을 사용한 이유는 코드의 간결함과 함수형 프로그래밍 스타일을 유지하면서 가독성과 유연성을 높이기 위함입니다.
  • 또한, Array.from()을 사용하면 배열을 생성하고 처리하는 과정을 한 번에 처리할 수 있어 for 루프보다 간단하고 직관적입니다.

그래서 이 코드에서는 for 루프 대신 Array.from()을 사용한 것입니다.


for 루프와 Array.from()

for 루프와 Array.from()은 각기 다른 목적과 상황에 따라 사용되며, 실제로는 둘 다 많이 쓰입니다. 하지만 언제 어떤 상황에서 더 많이 쓰이는지를 이해하는 것이 중요해요. 실제 웹/앱 개발에서 어떻게 사용되는지 알려드릴게요.

1. for 루프 사용 상황

  • 루프 제어가 필요할 때: for 루프는 초기화, 조건, 증감을 명시적으로 제어할 수 있어서 더 세밀한 루프 제어가 필요한 경우 많이 사용됩니다.
    • 예를 들어, 배열이 아닌 다른 데이터 구조나 범위를 순회하면서 중간에 특정 조건을 만족하면 루프를 중단하거나, 복잡한 계산을 수행해야 할 때는 for 루프가 적합합니다.

예시 (배열 외의 데이터 구조 순회):

for (let i = 0; i < 10; i++) {
    if (i === 5) break;  // 특정 조건에서 루프를 종료
    console.log(i);
}
  • 복잡한 조건을 가진 반복 작업: 단순한 배열 생성이 아닌, 복잡한 작업이 필요할 때도 for 루프가 적합할 수 있습니다. for는 더 유연하게 제어할 수 있어서 이런 상황에서 유용합니다.

2. Array.from() 사용 상황

  • 배열을 생성할 때: Array.from()배열을 생성하는 상황에서 매우 유용합니다. 특히, 배열의 길이가 정해져 있고, 그 배열의 각 요소를 특정 패턴에 맞게 만들고 싶을 때 자주 사용됩니다.
    • 예를 들어, 배열을 바로 생성하고 필터링하거나 변환해야 할 때는 Array.from()이 직관적이고 코드가 간결해집니다.

예시 (배열 생성 및 처리):

const arr = Array.from({ length: 10 }, (_, i) => i * 2);
// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
  • 함수형 프로그래밍이 필요할 때: 함수형 프로그래밍 패러다임을 따라 개발할 때, 배열을 처리하는 다양한 메서드(map, filter, reduce)와 결합해 사용할 수 있습니다. 이 방식은 코드의 가독성유지보수성을 높여줍니다.

예시 (함수형 프로그래밍 스타일):

const arr = Array.from({ length: 10 }, (_, i) => i + 1)
    .filter(n => n % 2 === 0)  // 짝수만 필터링
    .map(n => n * 2);  // 각 숫자를 2배로 변환

3. 실제 사용 비율

  • 일반적인 배열 순회에서는 여전히 for 루프가 많이 쓰입니다. 특히 초보자에게 익숙하고, 로직이 단순한 경우 for는 매우 직관적이기 때문에 널리 사용됩니다.
  • 배열 생성 및 처리의 경우에는 Array.from(), map(), filter() 같은 함수형 프로그래밍 방식이 많이 쓰입니다. 특히 React, Vue.js 같은 프론트엔드 프레임워크에서 데이터 변환 작업이 많기 때문에, 이러한 방식이 더 흔하게 보일 수 있습니다.

4. 언제 Array.from()을 사용해야 하는가?

  • 배열을 동적으로 생성해야 할 때
  • 배열을 순회하면서 간단한 변환 작업을 해야 할 때
  • 간결하고 읽기 쉬운 코드를 작성하고 싶을 때

결론:

  • for 루프는 여전히 많이 사용되고, 특히 복잡한 제어가 필요한 경우 유용합니다.
  • Array.from()은 배열 생성 및 변환에서 간결한 코드를 작성할 때 매우 유용하며, 특히 배열 작업이 많은 현대의 웹/앱 개발에서 많이 사용됩니다.

두 방법 모두 사용처에 따라 적절히 선택해서 사용하는 것이 좋습니다! for는 기본 중의 기본이라 여전히 중요한 개념이지만, Array.from()이나 map(), filter() 같은 함수형 메서드도 많이 활용해보시면 좋을 거예요.



3. 코드 3: replaceAll()을 사용한 방식

function solution(l, r, arr = []) {
    for (let i = l; i <= r; i++) {
        if (i.toString().replaceAll(/[05]/g, '') === '') arr.push(i);
    }
    return arr.length ? arr : [-1];
}

코드 설명:

  1. 숫자 변환: i를 문자열로 변환한 후, 정규 표현식 /[05]/g를 사용하여 '0'과 '5'를 빈 문자열로 대체합니다.
  2. 검사: 대체된 문자열이 빈 문자열('')인 경우에만 배열에 추가합니다. 즉, '0'과 '5'로만 이루어진 숫자인 경우에만 배열에 추가됩니다.
  3. 결과 반환: 배열이 비어 있으면 [-1]을 반환하고, 그렇지 않으면 결과 배열을 반환합니다.

사용된 메서드 설명:

  • replaceAll(): 문자열에서 특정 패턴을 모두 찾아 대체하는 메서드입니다.

    • 사용 예시:
      let str = "505";
      let replaced = str.replaceAll(/[05]/g, ''); // 결과: '' (빈 문자열)
  • 정규 표현식 /[05]/g: 문자열에서 '0'과 '5'를 찾아 모두 빈 문자열로 대체합니다. gglobal 플래그로, 문자열 전체에서 일치하는 모든 값을 대체

합니다.

장점:

  • 직관적: '0'과 '5'를 제거하고 빈 문자열인지를 확인하는 방식이 직관적입니다.
  • 간결함: replaceAll()을 사용해 비교적 간결하게 문제를 해결할 수 있습니다.

단점:

  • 비효율성: replaceAll()을 매번 호출해야 하므로, 성능이 떨어질 수 있습니다.
  • 성능: 범위가 매우 클 경우, replaceAll()을 반복 사용하는 것은 비효율적입니다.

보완점:

  • replaceAll() 대신, 정규 표현식을 한 번만 사용하는 방식으로 개선하면 성능을 향상시킬 수 있습니다.

4. 코드 4: every()를 사용한 방식

function solution(l, r) {
    const result = [];

    for (let i = l; i <= r; i++) {
        if ([...String(i)].every(num => num === "0" || num === "5")) {
            result.push(i);
        }
    }

    return result.length > 0 ? result : [-1];
}

코드 설명:

  1. 숫자 변환: i를 문자열로 변환한 후, 스프레드 연산자(...)를 사용해 각 숫자를 배열로 만듭니다.
  2. 검사: every()를 사용해 배열의 모든 요소가 '0' 또는 '5'인지 확인합니다.
  3. 결과 저장: 조건을 만족하는 숫자를 배열에 추가하고, 최종적으로 배열이 비어 있으면 [-1]을 반환합니다.

사용된 메서드 설명:

  • every(): 배열의 모든 요소가 조건을 만족하는지 검사합니다. 하나라도 조건을 만족하지 않으면 false를 반환합니다.

    • 사용 예시:
      let arr = ['0', '5'];
      let allZeroOrFive = arr.every(num => num === "0" || num === "5"); // true
  • 스프레드 연산자 ...: 문자열을 배열로 변환하는 데 사용되었습니다.

    • 사용 예시:
      let str = "505";
      let arr = [...str]; // ['5', '0', '5']

장점:

  • 간결함: every()와 스프레드 연산자를 사용해 코드를 간결하게 작성할 수 있습니다.
  • 직관적: '0'과 '5'로만 이루어진 숫자를 검사하는 방식이 직관적입니다.

단점:

  • 비효율성: every()로 배열을 검사하기 전에 String()과 스프레드 연산자로 변환하는 과정이 추가되므로, 범위가 큰 경우 성능에 영향을 미칠 수 있습니다.

보완점:

  • 문자열 변환과 배열 변환을 최소화하는 방식으로 개선할 수 있습니다.

5. 코드 5: 정규 표현식 test()를 사용한 방식

function solution(l, r) {
    var answer = [];

    for (var i = l; i <= r; i++) {
        if (/^[05]+$/.test(i)) {
            answer.push(i);
        }
    }

    return answer.length ? answer : [-1];
}

코드 설명:

  1. 정규 표현식 검사: i를 문자열로 변환하지 않고, 바로 정규 표현식 /^[05]+$/으로 검사하여 숫자가 '0'과 '5'로만 이루어져 있는지 확인합니다.
  2. 결과 저장: 조건을 만족하는 숫자를 배열에 추가하고, 최종적으로 배열이 비어 있으면 [-1]을 반환합니다.

사용된 메서드 설명:

  • test(): 정규 표현식을 사용해 문자열이 특정 패턴과 일치하는지 검사합니다. 패턴이 일치하면 true, 그렇지 않으면 false를 반환합니다.

    • 사용 예시:
      let regExp = /^[05]+$/;
      console.log(regExp.test("505")); // true
  • 정규 표현식 /^[05]+$/: 이 정규식은 '0'과 '5'로만 이루어진 문자열을 확인합니다.

    • ^: 문자열의 시작
    • [05]: '0'과 '5' 중 하나
    • +: 한 번 이상 반복
    • $: 문자열의 끝

장점:

  • 간결함: 정규 표현식을 사용해 한 줄로 '0'과 '5'만으로 이루어진 숫자를 검사할 수 있습니다.
  • 효율적: 문자열 변환 없이 바로 정규 표현식을 적용할 수 있어 비교적 효율적입니다.

단점:

  • 정규 표현식 이해: 정규 표현식을 모르는 경우에는 코드 이해가 어려울 수 있습니다.


죄송합니다. 더 명확하고 간결하게 설명드리도록 하겠습니다. 각 코드에서 사용된 메서드의 역할, 사용 방법, 그리고 코드 내에서의 사용 이유를 깔끔하게 다시 설명드리겠습니다.


6. 코드 6: SetisSubset() 함수 사용

function solution(l, r) {
    var answer = [];

    for (let i = l; i <= r; i++) {
        if (isSubset(new Set(i.toString().split('')), new Set(['0', '5']))) {
            answer.push(i);
        }
    }

    return answer.length ? answer : [-1];
}

function isSubset(setA, setB) {
    for (let elem of setA) {
        if (!setB.has(elem)) {
            return false;
        }
    }
    return true;
}

사용된 메서드:

  • Set:

    • 역할: 중복 없는 값의 집합을 저장합니다. 여기서는 i의 각 숫자(문자)가 '0'과 '5'로만 이루어졌는지 확인하는 데 사용됩니다.
    • 사용 방법: new Set()으로 집합을 생성합니다. set.has(값)으로 값이 집합에 포함되어 있는지 확인합니다.
    • 예시:
      const set = new Set([1, 2, 3]);
      console.log(set.has(2));  // true
  • split():

    • 역할: 문자열을 구분자로 나누어 배열로 변환합니다. 이 코드에서는 숫자를 문자열로 바꾼 후 각 자리를 나눕니다.
    • 사용 방법: 문자열.split(구분자)
    • 예시:
      let str = "505";
      console.log(str.split(''));  // ['5', '0', '5']

7. 코드 7: some()includes() 메서드 사용

function solution(l, r) {
    var answer = [];
    for (let i = l; i <= r; i++) {
        let tmp = i % 5 == 0 ? i : 0;
        let tmpstr = String(tmp);
        if (tmp != 0) {
            if (!tmpstr.split('').some(j => ['1','2','3','4','6','7','8','9'].includes(j))) {
                answer.push(i);
            }
        }
    }
    return answer.length ? answer : [-1];
}

사용된 메서드:

  • some():

    • 역할: 배열 내 하나라도 조건을 만족하는 요소가 있으면 true를 반환합니다. 여기서는 '1' ~ '9'가 포함되어 있는지 검사합니다.
    • 사용 방법: 배열.some(조건 함수)
    • 예시:
      let arr = [1, 2, 3];
      console.log(arr.some(num => num > 2));  // true
  • includes():

    • 역할: 배열에 특정 값이 포함되어 있는지 확인합니다.
    • 사용 방법: 배열.includes(값)
    • 예시:
      let arr = [1, 2, 3];
      console.log(arr.includes(2));  // true

8. 코드 8: 이진수 변환과 replaceAll() 사용

function solution(l, r) {
    var answer = [];
    let num = 5;
    let cnt = 1;
    let str;
    while (num <= r) {
        if (num >= l) {
            answer.push(num);
        }
        cnt++;
        str = (cnt).toString(2);  // 이진수로 변환
        str = str.replaceAll('1', '5');  // '1'을 '5'로 변환
        num = Number(str);  // 다시 숫자로 변환
    }

    return answer[0] ? answer : [-1];
}

사용된 메서드:

  • toString(2):

    • 역할: 숫자를 2진수 문자열로 변환합니다.
    • 사용 방법: 숫자.toString(진수)
    • 예시:
      let num = 5;
      console.log(num.toString(2));  // "101"
  • replaceAll():

    • 역할: 문자열에서 모든 일치하는 값을 대체합니다. 여기서는 이진수에서 '1'을 '5'로 변환합니다.
    • 사용 방법: 문자열.replaceAll(찾을값, 바꿀값)
    • 예시:
      let str = "101";
      console.log(str.replaceAll('1', '5'));  // "505"
  • Number():

    • 역할: 문자열을 숫자로 변환합니다.
    • 사용 방법: Number(문자열)
    • 예시:
      let str = "505";
      console.log(Number(str));  // 505

9. 코드 9: match() 메서드와 정규 표현식 사용

function solution(l, r) {
    var answer = [];
    let i = l;
    while (i <= r) {
        if (i % 5 != 0) {
            i++;
            continue;
        }
        if (String(i).match(/[^05]/)) {
            // '0'과 '5' 이외의 숫자가 있는 경우 스킵
        } else {
            answer.push(i);
        }
        i += 5;  // 5의 배수만 검사
    }

    return answer.length ? answer : [-1];
}

사용된 메서드:

  • match():
    • 역할: 문자열이 정규 표현식과 일치하는 부분을 반환합니다. 일치하지 않으면 null을 반환합니다.
    • 사용 방법: 문자열.match(정규 표현식)
    • 예시:
      let str = "507";
      console.log(str.match(/[^05]/));  // ["7"] ('0'과 '5' 이외의 숫자)

10. 코드 10: test() 메서드와 정규 표현식 사용

function solution(l, r) {
    const result = [];
    const reg = /[^(0|5)(0|5)$]/;

    for (let i = l; i <= r; i++) {
        if (!reg.test(`${i}`)) result.push(i);
    }

    return result.length ? result : [-1];
}

사용된 메서드:

  • test():

    • 역할: 문자열이 정규 표현식과 일치하는지 검사하고, 일치하면 true, 그렇지 않으면 false를 반환합니다.
    • 사용 방법: 정규표현식.test(문자열)
    • 예시:
      const regExp = /^[05]+$/;
      console.log(regExp.test("505"));  // true
      console.log(regExp.test("507"));  // false
  • 정규 표현식 /[^(0|5)(0|5)$]/:

    • 역할: '0'과 '5' 이외의 문자가 포함된 경우를 찾아냅니다.
    • 사용 예시:
      const reg = /[^(0|5)(0|5)$]/;
      console.log(reg.test("505"));  // false
      console.log(reg.test("507"));  // true


요약

1. 제일 쉽고 정확한 코드

선택: 코드 5 (정규 표현식을 사용한 코드)

function solution(l, r) {
    var answer = [];

    for (var i = l; i <= r; i++) {
        if (/^[05]+$/.test(i)) {
            answer.push(i);
        }
    }

    return answer.length ? answer : [-1];
}

이유:

  • 간결함: 코드가 매우 짧고 단순합니다. 정규 표현식 한 줄로 숫자가 '0'과 '5'로만 이루어졌는지 확인할 수 있어 가독성이 뛰어납니다.
  • 정확성: 정규 표현식이 '0'과 '5'로만 이루어진 숫자를 정확하게 찾아냅니다.
  • 효율성: 범위 내에서 모든 숫자를 확인하지만, 복잡한 로직 없이 쉽게 조건을 처리할 수 있습니다.
  • 사용성: 초보자도 정규 표현식의 동작 원리만 알면 쉽게 이해할 수 있습니다.

정리:

이 코드는 학습용이나 단순한 문제 해결에 가장 적합합니다. 쉽게 구현할 수 있고, 정규 표현식만으로 문제를 정확하게 처리할 수 있습니다.


2. 현업에서 (웹, 앱 개발) 사용할 수 있는 활용적인 코드

선택: 코드 2 (Array.from()filter() 사용한 코드)

function solution(l, r) {
    const result = Array.from({ length: r - l + 1 }, (_, i) => i + l)
        .filter(n => !/[^05]/.test(n));
    return result.length ? result : [-1];
}

이유:

  • 가독성: Array.from()filter()를 사용한 방식은 매우 직관적입니다. 이는 웹/앱 개발에서 중요한 가독성을 유지하면서 문제를 해결하는 좋은 예입니다.
  • 재사용성: 배열 생성과 필터링을 위한 메서드를 사용하여 다양한 조건이나 배열 처리 문제에 쉽게 적용할 수 있습니다. 특히 배열 처리가 많이 필요한 프론트엔드 개발에서 매우 유용합니다.
  • 확장성: 이 방식은 간단히 다른 조건이나 배열 구조로 확장할 수 있습니다. 프로젝트에서 데이터를 필터링하고, 가공해야 할 때 응용 가능성이 높습니다.
  • 유지보수: 함수형 메서드(filter())를 사용하여 유지보수가 용이하고, 직관적이므로 협업 시에도 쉽게 수정할 수 있습니다.

정리:

이 코드는 직관적이면서도 다양한 상황에 재사용할 수 있어 현업에서 실제 개발 중 데이터 필터링이나 처리할 때 매우 유용합니다.


3. 객관적으로 가장 정확하고, 에러가 없는 코드

선택: 코드 9 (5의 배수와 정규 표현식 match()를 사용한 코드)

function solution(l, r) {
    var answer = [];
    let i = l;
    while (i <= r) {
        if (i % 5 != 0) {
            i++;
            continue;
        }
        if (String(i).match(/[^05]/)) {
            // 다른 숫자가 있으면 아무 일도 하지 않음
        } else {
            answer.push(i);
        }
        i += 5;
    }

    return answer.length ? answer : [-1];
}

이유:

  • 정확성: 이 코드는 5의 배수만을 대상으로 하여 불필요한 검사를 줄이고, match()로 '0'과 '5' 이외의 숫자를 정확하게 걸러냅니다. 범위 내에서 '0'과 '5'로 이루어진 숫자만을 제대로 검출합니다.
  • 에러 방지: i % 5 == 0 조건을 먼저 걸러서 5의 배수 외에는 건너뛰므로 불필요한 작업을 최소화합니다. 이로 인해 논리적 오류가 발생할 가능성이 적습니다.
  • 효율성: 5의 배수만을 검사함으로써 다른 코드들보다 더 적은 계산량으로 문제를 해결할 수 있습니다.

정리:

이 코드는 정확하게 원하는 값만을 찾아내고, 논리적으로도 안정적입니다. 불필요한 작업을 줄여 최적화되어 있으며, 에러 없이 안전하게 동작하는 코드입니다.


결론:

  1. 제일 쉽고 정확한 코드: 코드 5 - 정규 표현식을 사용한 간결하고 직관적인 코드.
  2. 현업에서 사용할 수 있는 활용적인 코드: 코드 2 - Array.from()filter()를 사용한 직관적이고 확장성 높은 코드.
  3. 객관적으로 가장 정확하고 에러 없는 코드: 코드 9 - 5의 배수와 match()를 사용해 최적화된 논리적 코드.
profile
YOLO

0개의 댓글