[일일코딩 #35] String incrementer

Question

스트링에서 넘버만 따다가 +1 하는것. 자릿수 지키기.

https://www.codewars.com/kata/string-incrementer/javascript

Your job is to write a function which increments a string, to create a new string.

If the string already ends with a number, the number should be incremented by 1.
If the string does not end with a number. the number 1 should be appended to the new string.
Examples:

foo -> foo1

foobar23 -> foobar24

foo0042 -> foo0043

foo9 -> foo10

foo099 -> foo100

Attention: If the number has leading zeros the amount of digits should be considered.

My answer

정규식으로 number만 따다가 +1 해주고 원래 자릿수를 구해서 0을 그만큼 채워줬다.
마음에 안 듦.

function incrementString (str) {
    const numRegex = /[0-9]+/.exec(str);
    if (numRegex) {
        const numStr = numRegex.pop();

        const newNum = Number(numStr) + 1;
        const zeroNums = numStr.length - newNum.toString().length;
        const zeroStr = (zeroNums > 0) ? Array(zeroNums).fill("0").join("") : "";

        return str.replace(numStr, `${zeroStr}${newNum}`)
    }
    return `${str}1`;
}

Others' answer

Azuaron, Nakid..

function incrementString (input) {
    // 맨끝이 not a number면 그냥 + "1" 
    if(isNaN(parseInt(input[input.length - 1]))) return input + '1';

    // "001"을 넘겼다면 match는 001, p1은 00, p2는 1
    return input.replace(/(0*)([0-9]+$)/, function(match, p1, p2) {
        var up = parseInt(p2) + 1;

        // +1한 숫자 자릿수가 원본자릿수보다 크다면 0을 하나 뺀만큼 앞에 붙여주고 아니면 원본 0갯수만큼 붙여주기
        return (up.toString().length > p2.length) ? p1.slice(0, -1) + up : p1 + up;
    });
}
  • String.replace 첫번째 인자에 정규식을 넘길수 있고, 두 번째 인자에 함수를 넘길 수 있구나!
  • 정규식에 ()으로 그룹을 지어서 두번째 이후 인자로 받을 수 있구나
  • String.slice(0, -1)은 마지막 한글자를 떼는거구나

[일일코딩 #33] Remove Duplicates from Sorted Array

일일코딩 #32가 2017년 6월이었고, 오늘의 일일코딩 #33은 2019년 11월이다. 2년이 넘었다.
2년만에 푼 알고리즘 문제는 아주 노답이었다고 할 수 있다.
Leetcode easy난이도 문제인데 한시간 걸렸거든.
와 정말 부끄럽다.

영어 설명을 제대로 읽지 않은 탓도 있다.
Array의 duplicated된 엘리먼트를 지우라기에 당연히 지워진 array를 반환하는줄 알았지.
그런데 반환값은 Array의 length뿐이었고 Params로 온 Array의 reference를 받아 원본 Array를 중복 없이 만드는거였다. 메모리 새로 안쓰고.

담엔 잘 읽어.

Question

Leetcode 링크
Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length.

Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.

즉 아래와 같다.

removeDuplicates([1,1,2]); // returns 2 (원본 Array는 [1, 2]로 modify됨)

removeDuplicates([0,0,1,1,1,2,2,3,3,4]); // returns 4 (원본 Array는 [0, 1, 2, 3]로 modify됨)

My answer

While문을 돌면서 해당 element가 Array의 맨 마지막에 있는지 확인했다.
맨 마지막이면 살려주고 아니면 splice로 제거.
정렬된 배열이라 다 뭉쳐있을테니 각 뭉치의 제일 오른쪽 애들만 남긴다는 아이디어였다.

var removeDuplicates = function(nums) {
    var count = nums.length - 1;
    while(count >= 0) {
        if(count == nums.lastIndexOf(nums[count])) {
            count--;
        } else {
            nums.splice(count, 1);
        }
    }

    return nums.length;
};

잘 되긴 한다.

성능은 후지다. 이 퍼센트가 뭔가 했더니 하위 5%, 18%더라.

Runtime: 224 ms, faster than 5.75% of JavaScript online submissions for Remove Duplicates from Sorted Array.
Memory Usage: 37.8 MB, less than 18.75% of JavaScript online submissions for Remove Duplicates from Sorted Array.

처음에 param으로 받은 Array를 reference로 고쳐야한단걸 몰랐을때는
Array.prototype.reduce로 접근했다. prev, next를 돌며 앞뒤가 다르면 새로운 Array에 push해줬음. 그건 그것나름대로 제일 앞 or 제일 뒤가 edge case라는 헛점은 있더라 – 무튼 안됨.

다른 사람들은 어케 했는지 보자

Others’ answer

Best practice

var removeDuplicates = function(nums) {
    if (nums.length == 0) return 0;
    var i = 0;
    for (var j = 1; j < nums.length; j++) {
        if (nums[j] != nums[i]) {
            i++;
            nums[i] = nums[j];
        }
    }
    return i + 1;
}

첨엔 이해가 안갔다
이건 [0,0,1,1,1,2,2,3,3,4][ 0, 1, 2, 3, 4, 2, 2, 3, 3, 4 ]로 만들텐데 그럼 뒤에 더러운 숫자들이 남지 않는가?

근데 다시 Question을 읽어보니 뒤에 더러운 숫자가 있던 없던 반환한 lengthNum 길이만큼만 일치하면 되는거더라구.
문제가 여러모로 깔끔하지 못하네…

Answer by @Qfab

var removeDuplicates = function(nums) {
    nums.splice(0, nums.length, ...(new Set(nums)));
};

변태코드

광광 울며 정리하는 Javascript의 this

전역에서 함수를 할당하면 window로 들어가는구나. 그래서 그 속에서 this를 찍어보면 Window객체가 나온다. new Foo()로 초기화하면 예상했던 대로 Foo { }가 나오고. 면접에서 털리고 광광 울며 정리중

 
js에서 모든 함수는 실행시마다 함수 내부에 this가 추가. arguments라는 유사 배열 객체와 함께 암묵적으로 전달. 그렇기 때문에 함수가 호출된 상황에 따라 그 모습을 달리 함.

  • this: 함수의 현재 실행 문맥. js에선 4가지의 함수 실행 타입이 있기 때문
      1. 함수 실행: alert(‘hi’)
      1. 메소드 실행: console.log(‘hello’)
      1. 생성자 실행: new RegExp(”)
      1. 간접 실행: alert.call(undefined, ‘hello’)
    • 각각의 타입은 서로 다른 문맥을 가진다. 더욱이 strict mode또한 실행 문맥에 영향을 미친다.
  • 즉시실행함수 역시 함수 실행의 종류 중 하나
var greeting = (function(name) {
    return 'Hi' + name;
})('Jay');
console.log(greeting)
  • 함수 실행에서의 this는 전역 객체다. 전역 객체는 실행 환경에 따라 결정. 웹 브라우저에선 window가 전역 객체.
function sum(a, b) {
    console.log(this === window); //true
    this.myNum = 20; // this에 추가했고, 전역인 window에 추가됨
    return a + b;
}

function strictSum(a, b) {
    'use strict';
    console.log(this === undefined); //true
    return a + b;
}
sum(1, 2); // 3. 함수 호출 -> 여기서 this는 전역 객체
window.myNum; // 20. this에 추가했는데 윈도우에 추가됨
  • 엄격 모드에서의 함수 실행 this
    • 위의 예제에서 만약 위에 ‘use strict’;를 적어줬다면, this는 undefined가 된다.
    • 엄격 모드는 현재 스코프 뿐만 아니라 내부 스코프에도 적용됨
var numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       // this는 window, 엄격 모드였으면 undefined
       console.log(this === numbers); // => false
       return this.numberA + this.numberB; // numbers에 접근 불가
     }
     return calculate();
   }
};
// 이는 객체 내에 있는 메소드를 실행하는 것. 이 실행시의 this는 window
numbers.sum(); // NaN, 엄격 모드였으면 TypeError

refer: http://webframeworks.kr/tutorials/translate/explanation-of-this-in-javascript-1/

[일일코딩 #32] Two Sum

[일일코딩 #32] Two Sum

Question

링크
Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:
Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

My Answer

var twoSum = function(nums, target) {
  for (var i = 0; i &lt;nums.length; i++) {
    var lastIdx = nums.lastIndexOf(target - nums[i]);
    if(lastIdx &gt; 0) return new Array(i, lastIdx);
  }
};

lastIndexOf로 풀었다. 처음엔 Array 초기화 할 때 var answerArr = []로 해서 answerArr.push(a, b)로 넣었는데 코드 줄인다고 위처럼 바꿨더니 성능이 조금 더 줄었다. 변수 할당보다 new Array 로 하는게 더 오래걸리나보다.

Other’s answer

3가지 방법을 제시함.
1. 브루트 포스: 포문 2번 돌면서 뺀 값이 있나 찾기
2. Two pass hash table: 잘 이해 안감 왜 굳이?
3. One-pass hash table: 내가 푼 방법. 뺀 값이 map에 있나 확인

body overflow:hidden 이 모바일에서 안 먹을때 javascript로 해결방법

스크린샷 2017-05-09 오후 9.21.17

상황: 모바일에서 사이드 네비게이션이 열린 상태에서 스크롤시 body는 스크롤 되지 않게 하고싶다

Solution A: CSS

html,
body {
    overflow: hidden;
    position: relative;
    height: 100%;
}

PC에선 문제 없이 작동한다. 하지만 iOS 사파리랑 크롬에서는 무용지물.

Solution B: CSS – fix

body {
    position: fixed;
    width: 100%;
    height: 100%;
}

PC, 모바일 모두 되긴 하는데 Side nav를 닫은 후에는 position이 꼬임 -> DOM 클릭해도 이벤트가 활성화 안된다.

Solution C: jQuery

<body>
<div id="side-nav">사이드 네비게이션</div>
<div class="contents">여기에 body의 내용 넣는다</div>
</body>
$(function(){
    $('#side-nav').on('show.bs.offcanvas', function (e) {
      $('#sidenav-overlay').addClass('active');

      /* Disable scroll */
      $('.contents').on('scroll touchmove mousewheel', function(e){
        e.preventDefault();
        e.stopPropagation();
        return false;
      });
    });
    $('#sidenav-overlay').click(function() {
      $('#side-nav').offcanvas('hide');
      $('#sidenav-overlay').removeClass('active');

      /* Enable scroll */
      $('.contents').off('scroll touchmove mousewheel');
    });
  });

jQuery on handler로 스크롤을 막는다.
HTML에서 사이드 네비게이션과 Contents 영역을 분간해두면
사이드 네비게이션 자체에서는 스크롤 가능하게 할 수 있다.

Refer

http://stackoverflow.com/questions/3656592/how-to-programmatically-disable-page-scrolling-with-jquery

[Javascript]원하는 HTML 영역 프린트하기

스크린샷 2017-03-28 오전 10.48.19스크린샷 2017-03-28 오전 10.49.44

상황: 페이지의 일부만 프린트하고 싶다!

구글링 하면 2가지 방법이 나온다.
근데 둘다 내 상황에는 문제점이 있었다.

  1. 팝업으로 DOM을 복사해 프린트: 편하긴 한데 css, js가 떨어짐
  2. 전체 돔, 프린트 영역 원하는 돔 따로 저장한다음에 프린트영역만 살리고 프린트 후 전체 돔 살림: 편하긴 한데 js 이벤트 바인딩이 떨어짐

아래와 같이 display noneblock 을 toggle 해주는걸로 간단히 해결 가능하다.
React 사용중이라 jquery 말고 pure javascript로 코딩했다.

onPrint() {
const html = document.querySelector('html');
const printContents = document.querySelector('.modal-body').innerHTML;
const printDiv = document.createElement(&quot;DIV&quot;);
printDiv.className = &quot;print-div&quot;;

html.appendChild(printDiv);
printDiv.innerHTML = printContents;
document.body.style.display = 'none';
window.print();
document.body.style.display = 'block';
printDiv.style.display = 'none';
}

[javascript] 함수 할당시 실행 없이 인자 넘기기

Problem

var apiCRUD  = {
  downloadExcel: function($http) {
    //랄랄랄
  }
}

rc.downloadExcel = apiCRUD.downloadExcel($http);

apiCRUD.downloadExcel함수를 저렇게 rc.downloadExcel에 할당하면 바로 실행이 되는데,
나는 rc.downloadExcel이 호출되었을 때 apiCRUD의 함수가 실행되길 바란다.

그렇다고

rc.downloadExcel = apiCRUD.downloadExcel;

처럼 하면 $http인자를 못 넘긴다.

Solution

rc.downloadExcel = function() {
    apiCRUD.downloadExcel($http);
}

요러면 됨.
호이스팅 없이 함수 실행 시에 불리움.

Refer

j2p님의 도움 감사합니다.