본문 바로가기
JavaScript│Node js

마틴 파울러 - 리팩토링 Replace loop with Pipeline

by 자유코딩 2019. 11. 20.
const names = [];
for(const i of input) {
	if(i.job === "programmer")
    	names.push(i.name);
}
const names = input
	.filter(i => i.job === "programmer")
    .map(i => i.name)
;

for 문과 if문으로 구성된 코드를 filter 와 map으로 바꾼다.

 

motivation

파이프라인을 사용하면 코드를 더 간결하게 작성할 수 있다.

로직을 보기 쉽게 작성하자.

 

순서

 

예제

csv 파일을 읽는 아래 코드가 있다고 한다.

 

function acquireData(input) {
    const lines = input.split("\n");
    let firstLine = true;
    const result = [];
    for (const line of lines) {
        if (firstLine) {
            firstLine = false;
            continue;
        }
        if (line.trim() === "") continue;
        const record = line.split(",");
        if (record[1].trim() === "India") {
            result.push({ city: record[0].trim(), phone: record[2].trim() });
        }
    }
    return result;
}

 

 

 

function acquireData(input) {
    const lines = input.split("\n");
    let firstLine = true;
    const result = [];
    const loopItems = lines; // 1단계 반복할 배열을 복사한다.
    for (const line of loopItems) {
        if (firstLine) {
            firstLine = false;
            continue;
        }
        if (line.trim() === "") continue;
        const record = line.split(",");
        if (record[1].trim() === "India") {
            result.push({ city: record[0].trim(), phone: record[2].trim() });
        }
    }
    return result;
}

 

function acquireData(input) {
    const lines = input.split("\n");
    // let firstLine = true; 이부분 제거
    const result = [];
    const loopItems = lines.slice(1); // 2단계 slice 를 사용한다. 1번째 줄부터 시작
    for (const line of loopItems) {
        // if (firstLine) { // 여기도 제거
        //     firstLine = false;
        //     continue;
        // }
        if (line.trim() === "") continue;
        const record = line.split(",");
        if (record[1].trim() === "India") {
            result.push({ city: record[0].trim(), phone: record[2].trim() });
        }
    }
    return result;
}

 

여기서 firstLine은 반복문에서 데이터를 조작하기 위한 변수였다.

slice 를 사용함으로써 이 변수를 제거 할 수 있게 되었다.

 

function acquireData(input) {
    const lines = input.split("\n");
    // let firstLine = true; 이부분 제거
    const result = [];
    const loopItems = lines
    		.slice(1)
            .filter(line => line.trim() !== ""); // filter도 사용한다. 3단계
    for (const line of loopItems) {
        // if (firstLine) { // 여기도 제거
        //     firstLine = false;
        //     continue;
        // }
        // if (line.trim() === "") continue; 3단계 빈 문자열 읽는 부분 제거
        const record = line;
        if (record[1].trim() === "India") {
            result.push({ city: record[0].trim(), phone: record[2].trim() });
        }
    }
    return result;
}

filter도 사용해서 공백을 제거 했을때 빈 문자열이 아닌 것들을 선별한다.

csv 를 읽는 과정에서 빈 문자열을 읽는 부분도 제거 할 수 있다.

 

한 줄씩 line 이 record에 대입되고  record의 첫번째 항목이 India와 같은지 비교한다.

같다면 result 에 push한다.

이제 이 부분을 고친다.

 

function acquireData(input) {
    const lines = input.split("\n");
    // let firstLine = true; 이부분 제거
    const result = [];
    const loopItems = lines
            .slice(1) // 첫번째 줄부터
            .filter(line => line.trim() !== "") // 공백이 아닌 줄을 찾는다.
            .map(line => line.split(",")); // , 를 기준으로 분리한다.
            // 한 줄씩 , 기준으로 분리된 것이 배열로 저장된다.
    for (const line of loopItems) {
        // if (firstLine) { // 여기도 제거
        //     firstLine = false;
        //     continue;
        // }
        // if (line.trim() === "") continue; 
        const record = line;
        // if (record[1].trim() === "India") { // 이 부분을 제거한다
            result.push({ city: record[0].trim(), phone: record[2].trim() });
        // }
    }
    return result;
}

 

제거 했다면 Map 을 사용한다.

 

function acquireData(input) {
    const lines = input.split("\n");
    // let firstLine = true; 이부분 제거
    const result = [];
    const loopItems = lines
            .slice(1)
            .filter(line => line.trim() !== "")
            .map(line => line.split(","))
            .filter(record => record[1].trim() === "India")
            .map(record => ({ city: record[0].trim(), phone: record[2].trim()}));// map 을 사용한다.
    for (const line of loopItems) {
        const record = line;
        result.push(line);
    }
    return result;
}

 

 

이제 India인지 비교하는 부분도 filter로 들어갔다.

아래쪽 for문에서 하나씩 위쪽 filter, map으로 추가했다.

for 문에서는 result에 push 하는게 주된 역할이다.

push는 결국 배열을 만들려고 하는 일인데 이건 map이 이미 하고 있어서 지울 수 있다.

 

function acquireData(input) {
    const lines = input.split("\n");
    // let firstLine = true; 이부분 제거
    // const result = [];
    const result = lines
            .slice(1)
            .filter(line => line.trim() !== "")
            .map(line => line.split(","))
            .filter(record => record[1].trim() === "India")
            .map(record => ({ city: record[0].trim(), phone: record[2].trim()}));// map 을 사용한다.
    // for (const line of loopItems) { // 여기를 지운다
    //     const record = line;
    //     result.push(line);
    // }
    return result;
}

 

 

이렇게 되면 result 라는 변수는 return 할때만 쓰인다.

이것도 없앨 수 있다.

 

 

function acquireData(input) {
    const lines = input.split("\n");
    // let firstLine = true; 이부분 제거
    // const result = [];
    return lines
            .slice(1)
            .filter(line => line.trim() !== "")
            .map(line => line.split(","))
            .filter(record => record[1].trim() === "India")
            .map(record => ({ city: record[0].trim(), phone: record[2].trim()}));// map 을 사용한다.
    // for (const line of loopItems) { // 여기를 지운다
    //     const record = line;
    //     result.push(line);
    // }
    // return result;
}

 

책에서는 lines도 없앨까 했지만 없애지 않았다고 한다.

그 이유는 lines 라는 변수는 여기서 무슨 일이 일어나는지 설명하는데 도움이 되기 때문이다.

 

댓글