JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks
、No Compilers
、No Libraries
、No Boilerplate
在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
練習一些 Array 常用的方法,包括 filter()、sort()、map()、reduce()。
前七個練習題要使用的資料如下:
/*JS*/
// Get your shorts on - this is an array workout!
// ## Array Cardio Day 1
// Some data we can work with
const inventors = [
{ first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
{ first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
{ first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
{ first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
{ first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
{ first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
{ first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
{ first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
{ first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
{ first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
{ first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
{ first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
];
const people = [
'Bernhard, Sandra', 'Bethea, Erin', 'Becker, Carl', 'Bentsen, Lloyd', 'Beckett, Samuel', 'Blake, William', 'Berger, Ric', 'Beddoes, Mick', 'Beethoven, Ludwig',
'Belloc, Hilaire', 'Begin, Menachem', 'Bellow, Saul', 'Benchley, Robert', 'Blair, Robert', 'Benenson, Peter', 'Benjamin, Walter', 'Berlin, Irving',
'Benn, Tony', 'Benson, Leana', 'Bent, Silas', 'Berle, Milton', 'Berry, Halle', 'Biko, Steve', 'Beck, Glenn', 'Bergman, Ingmar', 'Black, Elk', 'Berio, Luciano',
'Berne, Eric', 'Berra, Yogi', 'Berry, Wendell', 'Bevan, Aneurin', 'Ben-Gurion, David', 'Bevel, Ken', 'Biden, Joseph', 'Bennington, Chester', 'Bierce, Ambrose',
'Billings, Josh', 'Birrell, Augustine', 'Blair, Tony', 'Beecher, Henry', 'Biondo, Frank'
];
Array.prototype.filter()
filter()
方法會透過指定的函式運算,決定保留哪些資料並形成一個新的陣列。
在 filter()
裡 return true 代表要保留該筆資料,除此之外的資料都會被捨棄,我們沒必要再寫 else 去 return false。
以下我們透過 filter()
過濾"出生於16世紀的發明家"。
/*JS*/
const fifteen = inventors.filter(function(inventor){
if(inventor.year >= 1500 && inventor.year < 1600){
return true; //keep it in the new array
}
})/*除了有回傳true之外的東西都會被丟掉,不必再用else return false*/
更簡潔的寫法:
console.table()
,可以將 Array 以表格的形式呈現出來。
/*JS*/
const fifteen = inventors.filter(inventor => (inventor.year >= 1500 && inventor.year < 1600));
console.table(fifteen);
Array.prototype.map()
map()
方法會建立一個和原陣列長度相同的陣列,其內容為原陣列的每一個元素經由指定的函式運算後所回傳的結果之集合。
以下我們使用 map()
方法,將發明家的 first name 和 last name 結合在一起,形成一個新的陣列。
/*JS*/
const fullNames = inventors.map(inventor => `${inventor.first} ${inventor.last}`); /*Template literals*/
console.table(fullNames);
Array.prototype.sort()
sort()
方法會對一個陣列中的所有元素進行排序,並回傳此陣列,預設是以字串的 Unicode 編碼進行排序。
我們也可以指定一個compareFunction
,用自訂的排序規則為陣列進行排序。compareFunction(a, b)
,回傳值小於0時,a 會被排在 b 的前面,反之若回傳值大於0時,a 會被排在 b 的後面。
以下我們使用 sort()
,將較早出生的發明家排在前面的位置。
const ordered = inventors.sort(function(a,b){
if(a.year > b.year){
return 1; /*a 往後排*/
}else{
return -1; /*a 往前排*/
}
})
更簡潔的寫法:
const ordered = inventors.sort((a,b) => a.year > b.year ? 1 : -1);
console.table(ordered);
Array.prototype.reduce()
reduce()
方法將一個累加器(accumulator)及陣列中每項元素傳入函式中進行運算,將陣列化為單一的值。
其中一種用法如下:
Array.reduce((accumulator,currentValue) => {
return accumulator + currentValue;
},initialValue); /*initialValue 是 accumulator 的初始值*/
另一種用法如下:
const totalYears = inventors.reduce((total,inventor)=>{
return total + (inventor.passed - inventor.year);
},0);
console.log(totalYears);
以下我們將這些發明家依照壽命的長短由大到小進行排序。
透過 passed - year
,我們可以很簡單的算出每一位發明家的壽命。
const oldest = inventors.sort(function(a,b){
const lastGuy = a.passed - a.year;
const nextGuy = b.passed - b.year;
return lastGuy > nextGuy ? -1 : 1;
});
console.table(oldest)
這題我們需要自行到 https://en.wikipedia.org/wiki/Category:Boulevards_in_Paris 右鍵檢查進入 console 進行解題。
Node.textContent
可以取得節點或其後代的文字內容。
Array.prototype.includes()
includes()
方法會判斷陣列是否包含特定的元素,並以此來回傳 true
或 false
。
Array.from()
從類陣列(array-like,例如: NodeList)或是可迭代(iterable)物件建立一個新的 Array
實體。
首先,我們取得包覆所有巴黎大道名稱的最上層 div 標籤(.mw-category
)。接著,取得在其之下的所有連結(a
)。最後,使用 map()
方法以所有連結的內部文字組成新陣列並透過 filter()
過濾出包含(include)'de'的所有大道名稱。
const category = document.querySelector('.mw-category');
const links = Array.from(category.querySelectorAll('a'));/*NodeList*/
const de = links.map(link => link.textContent).filter(streetName => streetName.includes('de'));
console.table(de);
String.prototype.split()
split()
方法可以使用指定的分隔字元(separator)將一個String
分割成串列。
以下用,
(逗號和空格)作為分隔字元將 first name、last name 切開並放入一個陣列中,接下來就可以依照 aLast、bLast 的 Unicode 進行 last name 的排序(由小到大)。
const alpha = people.sort(function(lastOne,firstOne){
const [aLast,aFirst] = lastOne.split(', ');
const [bLast,bFirst] = firstOne.split(', ');
return aLast > bLast ? 1 : -1;
});
另一種寫法:
const alpha = people.sort((lastOne,firstOne) => {
const [aLast,aFirst] = lastOne.split(', ');
const [bLast,bFirst] = firstOne.split(', ');
return aLast > bLast ? 1 : -1;
});
資料內容:
const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car', 'truck' ];
一開始賦予累加器(obj)的初始值(initial value)為空物件({}),因為最初所有的 item 都不存在於 obj 中,所以透過 if 判斷當 obj[item] 不存在時,就建立 obj[item] 並賦予初始值0。其後就可正常將 item 持續累加至正確的交通工具分類中(obj[item]++)。
const transportation = data.reduce(function(obj,item){
if(!obj[item]){
obj[item] = 0;
}
obj[item]++;
return obj;
},{})
console.table(transportation);