문제 설명

길이가 같은 두 1차원 정수 배열 a, b가 매개변수로 주어집니다. a와 b의 내적을 return 하도록 solution 함수를 완성해주세요.

이때, a와 b의 내적은 a[0]*b[0] + a[1]*b[1] + ... + a[n-1]*b[n-1] 입니다. (n은 a, b의 길이)


제한사항
  • a, b의 길이는 1 이상 1,000 이하입니다.
  • a, b의 모든 수는 -1,000 이상 1,000 이하입니다.

입출력 예

입출력 예 설명

입출력 예 #1

  • a와 b의 내적은 1*(-3) + 2*(-1) + 3*0 + 4*2 = 3 입니다.

입출력 예 #2

  • a와 b의 내적은 (-1)*1 + 0*0 + 1*(-1) = -2 입니다.
  •  

내 코드

function solution(a, b) {
    let answer = 0;
    for(let i = 0 ; i < a.length ; i++){
        answer += a[i] * b[i];
    }
    return answer;
}

 

728x90

NaN 이란 무엇인가?

NaN은 Not a Number ( 숫자가 아님 )을 나타낸다.

이번에 인턴일로 맡은 작업에서 NaN을 되게 많이 맞닥뜨렸는데, NaN이 무엇을 의미하는지 명확하게 몰랐었어서 난감했었다.

이번에 NaN으로 고생하기 전까지만해도 NaN의 개념을 undefiend나 null과 비슷한 개념이라고 생각을 하고 있었고, NaN인지 판단하는 로직 또한 일반적인 === (Strict equality) 연산자로 비교할 수 있는 줄 알았다.

 

내가 이번 프로젝트에서 에러사항을 겪었던 로직과 유사하게 코드를 작성해 보았다.

아래 코드의 결과는 무엇일까?

나처럼 NaN이 무엇인지 명확하게 모르거나 NaN을 처음 접해보는 사람은 당연히 "a is not number"가 출력될 것이라고 생각할 것이다.

( 아님 말고 ! )

const a = NaN;

if( a === NaN || a === undefined || a === '0' ) {
	console.log("a is not number");
}
else{
	console.log("a is number");
}

그러나 이 코드를 실제로 돌려보면 아래와 같이 "a is number"라는 결과가 나오는 것을 알 수 있다.

왜 이러한 결과가 나오는 것일까 ? 내가 원하는 결과는 "a is not number"인데 !!

처음에는 | a === NaN | 비교 구문이 문제가 있다는 것을 파악하지 못했어서 조금 시간이 걸렸었다.

디버깅 하면서 NaN을 비교하는 구문이 제대로 실행되지 않았다는 것을 파악하고, NaN에 대해 먼저 구글링해보았다.

 

MDN 문서에 따르면 NaN은 전역 스코프의 변수로, NaN의 초기값은 Not-A-Number로, Number.NaN의 값과 같다고 한다.

최신 브라우저에서 NaN은 설정 불가, 쓰기 불가 속성이라고 한다. 

 

NaN 판별

NaN은 다른 모든 값과 비교 ( == , !=, ===, !== )했을 때 같지 않으며(false), 다른 NaN과도 같지 않다.

NaN의 판별은 오로지 Number.isNaN() 또는 isNaN()을 사용해야 제일 분명하게 수행할 수 있다. 아니면 오로지 NaN만이 자기 자신과 비교했을 때 같지 않음을 이용할 수도 있다.

 

아래 예제를 통해 더 쉽게 이해할 수 있다.

NaN === NaN;        // false
Number.NaN === NaN; // false
isNaN(NaN);         // true
isNaN(Number.NaN);  // true

function valueIsNaN(v) { return v !== v; }
valueIsNaN(1);          // false
valueIsNaN(NaN);        // true
valueIsNaN(Number.NaN); // true

출처 : MDN

 

그러나 isNaN()과 Number.isNaN()의 차이점을 기억해 두어야 한다. isNaN은 현재 값이 NaN이거나, 숫자로 변환했을 때 NaN이 되면 참을 반환하지만, Number.isNaN은 현재 값이 NaN 이어야만 참을 반환한다.

const an = '8';
const nan = 'c';

console.log(isNaN(an)); // false
console.log(isNaN(nan)); // true

console.log(Number.isNaN(an)); // false
console.log(Number.isNaN(nan)); // false

 

728x90

VanillaJS를 이용하여 Tooltip Input Component 만들기

/**
 * (required) type : number | text
 * (required) id : text
 * (optional) blank : boolean
 * (optional) blankMessage : text
 * (optional) disabled: boolean
 * (optional) regExp : tooltip message view condition : regExp ( 양끝 / 제거한 값을 넣어줘야함 )
 * (optional) regExpMessage : tooltip message
 * (optional) frontLabel : text
 * (optional) backLabel : text
 * * */
class TooltipInput extends HTMLElement {
  /**
     * constructor : 최초 실행 생성 Method
     * */
  constructor() {
    super();
    this.render();
  }

  /**
     * render() : 해당 tag에 넣을 html tag를 rerendering한다.
     * */
  render() {
    this.innerHTML = this.getTemplate();
  }

  /**
     * 해당 컴포넌트가 mount될 때 불러진다.
     * * */
  connectedCallback() {
    const attrList = this.attributes;
    const input = this.getElementsByClassName('tooltip-input');

    let front = attrList.frontLabel;
    let back = attrList.backLabel;

    if (front === undefined) {
      front = this.getElementsByClassName('front');
      front[0].className = 'display-none';
    }
    if (back === undefined) {
      back = this.getElementsByClassName('back');
      back[0].className = 'display-none';
    }

    /**
         * input eventListener
         * * */
    input[0].addEventListener('input', (e) => {
      const { blank } = e.path[3].attributes;
      const { blankMessage } = e.path[3].attributes;
      const rex = e.path[3].attributes.regExp;
      const rexMessage = e.path[3].attributes.regExpMessage;

      /* input.value 빈칸인 경우 */
      if (e.currentTarget.value.length === 0) {
        if (blank !== null && blank !== undefined && (blank.value === 'true' || blank.value === '')) {
          const tooltipMessage = this.getElementsByClassName('tooltip-message');
          tooltipMessage[0].className = 'tooltip-message';
          if (blankMessage === undefined) {
            tooltipMessage[0].innerHTML = '값을 입력해주세요.';
          } else {
            tooltipMessage[0].innerHTML = blankMessage.value;
          }

          console.log(tooltipMessage[0].classList);
        }
      } else {
        /* input.value 빈칸이 아닌 경우 */
        /* attr 에 정규식이 있는 경우 */
        if (rex !== null && rex !== undefined) {
          const regExp = new RegExp(rex.value);
          console.log(!regExp.test(e.currentTarget.value));
          if (!regExp.test(e.currentTarget.value)) {
            // 정규식 만족 X
            const tooltipMessage = this.getElementsByClassName('tooltip-message');
            tooltipMessage[0].className = 'tooltip-message';
            if (rexMessage !== undefined && rexMessage !== null) {
              tooltipMessage[0].innerHTML = rexMessage.value;
            } else {
              tooltipMessage[0].innerHTML = '조건을 만족하지 않습니다.';
            }
          } else if (blank !== null && blank !== undefined && (blank.value === 'true' || blank.value === '')) {
            const tooltipMessage = this.getElementsByClassName('tooltip-message');
            tooltipMessage[0].className = 'tooltip-message display-none';
          }
        }
      }
    });

    /**
         * disabled 값이 존재하는지 체크
         * disabled 값이 존재 == true 인경우 input의 disabled attr 활성화
         * disabled 값이 존재하지 않거나 ( null ) false인 경우 input의 disabled attr 비활성화(삭제)
         * * */

    if (attrList.getNamedItem('disabled') !== null && (attrList.getNamedItem('disabled').value === 'true' || attrList.getNamedItem('disabled').value === '')) {
      /**
             * disabled 값이 true인 경우 input의 disabled attr 활성화
             * */
      input[0].setAttribute('disabled', 'disabled');
    }

    /**
         * input값이 빈칸인 경우 확인해야하는지 체크
         * blank === true && blankMessage === undefined => "입력값이 필요합니다."
         * blank === true && blankMessage !== undefined => "blankMessage"
         * * */

    if (attrList.getNamedItem('blank') !== null && (attrList.getNamedItem('blank').value === 'true' || attrList.getNamedItem('blank').value === 'blank')) {
      /**
             * blank === true
             * * */
      if (input[0].value === '') {
        const tooltipMessage = this.getElementsByClassName('tooltip-message');
        if (attrList.getNamedItem('blankMessage') !== null) {
          // blank === true && blankMessage === undefined => "입력값이 필요합니다."
          tooltipMessage[0].innerHTML = attrList.getNamedItem('blankMessage').value;
        } else {
          // blank === true && blankMessage !== undefined => "blankMessage"
          tooltipMessage[0].innerHTML = '입력값이 필요합니다.';
        }
      }
    }
  }

  /**
     * 해당 tag에 attributes를 observing한다.
     * * */
  static get observedAttributes() {
    return ['type', 'id', 'blank'];
  }

  /**
     * getTemplate() : rendering될 html tag에 대한 template이다.
     * * */
  getTemplate() {
    let frontLabel;
    let backLabel;

    const type = this.attributes.type.value || '';
    const id = this.attributes.id.value || '';

    frontLabel = this.attributes.frontLabel !== undefined ? this.attributes.frontLabel.value : '';
    backLabel = this.attributes.backLabel !== undefined ? this.attributes.backLabel.value : '';

    return `
            <style>
                input[type="number"]::-webkit-outer-spin-button,
                input[type="number"]::-webkit-inner-spin-button {
                    -webkit-appearance: none;
                    margin: 0;
                }
                
                div{
                    width : 100%;
                }
                
                .tooltip-input{
                    width : 100%;
                    height : 26px;
                    border-radius: 4px;
                    box-shadow: inset 0 3px 2px 0 rgba(49,49,49,0.13);
                    border : solid 1px #b4b4b4;
                    background: #fff;
                    padding-left : 12px;
                    
                    /* font */
                    font-style : normal;
                    font-size : 12px;
                    font-family: 'Roboto';
                    color : #6e6e6e;
                }
                
                .label-text{
                    font-style : normal;
                    font-size : 12px;
                    font-weight : 600;
                    font-family: 'Roboto';
                    color : #6e6e6e;
                }
                
                .disabled{
                    background: #e6e6e6;
                }
                
                .display-none{
                    display : none;
                }
                
                .display-block{
                    display: block;
                }
                
                .tooltip-message{
                    width : auto;
                    height : 16px;
                    border : 1px solid #ede381;
                    background: #f7f2c0;
                    color : #9d870e;
                    
                    white-space: nowrap;
                    
                    position : absolute;
                    right : -15px;
                    top : -32px;
                    padding : 7px 13px 7px 18px;
                    z-index : 30; 
                }
                
                .tooltip-box{
                    min-width : 50px;
                    display: block;
                    position : relative;
                }
                
                .tooltip-box-container{
                    width : 100%;
                    display: flex;
                    flex-direction: row;
                    align-items: center;
                }
                
                .front{
                    width : auto;
                    white-space: nowrap;
                    margin-right : 10px;
                }
                
                .back{
                    width : auto;
                    white-space: nowrap;
                    margin-left : 25px;
                }
                
            </style>
            
            <div class="tooltip-box-container">
                <label for="${id}" class="label-text front">${frontLabel}</label>
                <div class="tooltip-box">
                    <input id="${id}" type="${type}" class="tooltip-input"/>
                    <span class="tooltip-message display-none"></span>
                </div>
                <label for="${id}" class="label-text back">${backLabel}</label>
            </div>
        `;
  }

  /**
     * 해당 컴포넌트가 unmount될 때 불리어 진다.
     * * */
  disconnectedCallback() {

  }

  /**
     * attributes가 수정된다면 수정된 갯수만큼 불리어 진다.
     * * */
  attributeChangedCallback(name, oldValue, newValue) {

  }
}

/**
 * tool-tip 이라는 tag가 customElements에 있는가? 없다면 새롭게 define하라는 코드로,
 * 동일 tag가 2번 define하면 오류가 날 수 있으므로, component가 여러차례 사용되면서 define되는 것을 막기 위한 용도이다.
 * * */

// Define the new web component
if ('customElements' in window) {
  customElements.define('tooltip-input', TooltipInput);
}

과정과 더 자세한 설명은 다음에 쓸 예정

 

참고 블로그 링크 

 

728x90

'Language > Javascript' 카테고리의 다른 글

[ Javascript ] Set Object  (0) 2022.11.02
[ Javascript ] setTimeout, setInterval  (0) 2022.07.27
[ Javascript ] Array.prototype Methods  (0) 2022.03.08
[ Javascript ] async/await  (0) 2022.03.07
[ Javascript ] Promise  (0) 2022.03.06

문제 설명

문자열 s를 숫자로 변환한 결과를 반환하는 함수, solution을 완성하세요.

제한 조건
  • s의 길이는 1 이상 5이하입니다.
  • s의 맨앞에는 부호(+, -)가 올 수 있습니다.
  • s는 부호와 숫자로만 이루어져있습니다.
  • s는 "0"으로 시작하지 않습니다.
입출력 예

예를들어 str이 "1234"이면 1234를 반환하고, "-1234"이면 -1234를 반환하면 됩니다.
str은 부호(+,-)와 숫자로만 구성되어 있고, 잘못된 값이 입력되는 경우는 없습니다.


function solution(s) {
    return Number(s);
}

 

728x90

+ Recent posts