(Appleton 잘 지내닝,,)
미국의 도시들에 대한 정보가 각각의 요소로 들어가 있는 데이터를 가져와 사용자가 입력하는 글자와 일치하는 도시나 주를 suggestion으로 보여주는 웹페이지. 입력을 할 때마다 업데이트가 되어야 하고 일치하는 글자에 대한 하이라이트도 추가해야한다.
<form class="search-form">
<input type="text" class="search" placeholder="City or State" />
<ul class="suggestions">
<li>Filter for a city</li>
<li>or a state</li>
</ul>
</form>
const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
const cities = [];
fetch(endpoint)
.then(blob => blob.json())
.then(data => cities.push(...data));
function findMatches(wordToMatch, cities) {
return cities.filter(place => {
// here we need to figure out if the city or state matches what was searched
const regex = new RegExp(wordToMatch, 'gi');
return place.city.match(regex) || place.state.match(regex)
});
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function displayMatches() {
const matchArray = findMatches(this.value, cities);
const html = matchArray.map(place => {
const regex = new RegExp(this.value, 'gi');
const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
return `
<li>
<span class="name">${cityName}, ${stateName}</span>
<span class="population">${numberWithCommas(place.population)}</span>
</li>
`;
}).join('');
suggestions.innerHTML = html;
}
const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');
searchInput.addEventListener('change', displayMatches);
searchInput.addEventListener('keyup', displayMatches);
cities라는 빈 배열을 만들어두고 이제 여기에 데이터를 가져와 넣을 것이다.
const cities = [];
fetch(endpoint)
.then((blob) => blob.json())
.then((data) => cities.push(data));
이렇게 하면 될 것 같지만 문제가 생긴다. 데이터가 nested 되어 들어와서 불필요한 층이 생겨버린다. 이렇게 cities 배열에 데이터들이 들어올 때 데이터의 요소들이 하나 하나 들어오는 게 아니라 통째로 들어와버리기 때문이다.
이런 불필요한 nesting을 막으려면 스프레드 문법을 사용하면 된다. 스프레드 문법을 사용하면 묶여있던 배열을 개별적인 요소로 펼쳐져 우리가 원하던 것처럼 배열의 요소들이 cities에 하나하나 잘 들어간다.
const cities = [];
fetch(endpoint)
.then((blob) => blob.json())
.then((data) => cities.push(...data));
개발자 도구에서 cities 배열을 열어보면 nesting 없이 원하는 배열이 바로 나타난다.
사용자의 입력값과 일치하는 값이 cities 데이터에 있는지 찾아보고자 할 때 나는 includes
메소드를 사용해서 찾고자 했는데 영상에서는 정규표현식을 만들어 match
메소드를 사용하는 방식으로 일치하는 글자를 찾아냈다.
정규표현식을 생성하는 방법에는 두 가지가 있는데 두 가지 모두 이번 과제에 사용이 되었다. 우선 첫 번째 방법은 new
키워드를 통한 생성자 방식. 첫 번째 인수로는 '표현의 패턴', 그리고 두 번째 인수로는 '패턴을 어떠한 방식으로 검색할 것인지에 대한 옵션'을 받는다.
new RegExp('표현', '옵션')
function findMatches(wordToMatch, cities) {
return cities.filter((place) => {
const regex = new RegExp(wordToMatch, "gi"); // 생성자 방식
return place.city.match(regex) || place.state.match(regex);
});
}
두 번째 방식은 리터럴 방식이다. 생성자 방식보다 더 간단하게 /
를 사용해서 만들 수 있다. 마침 이 방식도 과제에서 인구수를 쉼표 형식의 숫자로 나타내도록 만드는 함수에 사용되었다.
/표현/옵션
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
정규표현식을 공부해본 적이 없어 너무 낯설었지만 이참에 어떻게 사용되는지 간단하게나마 공부했다. 이 글에는 만드는 방식 정도만 기록해두었고 이 영상을 보면서 연습을 좀 해봤다. 앞으로 문자형을 다루어야 할 때 요긴하게 쓰일 듯.
join()
메소드는 배열의 모든 요소를 연결해 하나의 문자열로 만든다.
const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join());
// Expected output: "Fire,Air,Water"
console.log(elements.join(''));
// Expected output: "FireAirWater"
console.log(elements.join('-'));
// Expected output: "Fire-Air-Water"
아규먼트로 배열의 각 요소를 구분할 문자열을 지정하면 위의 예시처럼 배열 사이사이마다 지정해준 문자열이 들어가 원하는 형식으로 나타낼 수 있다. ''
를 쓰면 단순히 붙여버린다.
오늘 과제에서는 이렇게 suggestion 리스트 각각의 요소들이 부자연스럽게 ,
로 나뉘어져 버리는 문제를 해결하기 위해 .join()
메소드가 사용되었다.
function displayMatches() {
const matchArray = findMatches(this.value, cities);
const html = matchArray
.map((place) => {
return `
<li>
<span class="name">${place.city}, ${place.state}</span>
<span class="population">${place.population}</span>
</li>
`;
})
.join(""); // Array.prototype.join()
suggestions.innerHTML = html;
}
잘 해결되었다.
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
숫자를 콤마 형식으로 변환해주는 함수다. 예컨대 123456이라는 숫자를 123,456의 형식으로 변환해준다.
replace()
메소드가 사용되었는데 이 메소드는 문자열에서 특정 문자열을 치환하는 메소드다. 아규먼트의 첫 번째 값에는 바꿀 대상이 되는 패턴이 들어가는데 일반 문자열 또는 정규표현식이 들어갈 수 있다. 아규먼트의 두 번째 값에는 새롭게 바꿀 문자열이나 함수가 들어갈 수 있다. 아래 예시 참고.
const paragraph = "I think Ruth's dog is cuter than your dog!";
console.log(paragraph.replace("Ruth's", 'my'));
// Expected output: "I think my dog is cuter than your dog!"
const regex = /Dog/i;
console.log(paragraph.replace(regex, 'ferret'));
// Expected output: "I think Ruth's ferret is cuter than your dog!"