30 天精通 RxJS (03): Functional Programming 通用函式

30 天精通 RxJS (03): Functional Programming 通用函式

了解 Functional Programming 的通用函式,能讓我們寫出更簡潔的程式碼劫流,也能幫助我們學習 RxJS鞠眉。

這是【30天精通 RxJS】的 03 篇,如果還沒看過 02 篇可以往這邊走:
30 天精通 RxJS (02): Functional Programming 基本觀念

讀者可能會很好奇涌攻,我們的主題是 RxJS 為什麼要特別講 Functional Programming 的通用函式呢? 實際上,RxJS 核心的 Observable 操作觀念跟 FP 的陣列操作是極為相近的乌企,只學會以下幾個基本的方法跟觀念后,會讓我們之后上手 Observable 簡單很多成玫!

今天的程式碼比較多加酵,大家可以直接看影片!

視頻

ForEach

forEach 是 JavaScript 在 ES5 后哭当,原生就有支援的方法猪腕。

原本我們可能要透過 for loop 取出陣列中的每一個元素

var arr = ['Jerry', 'Anna'];

for(var i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

現(xiàn)在可以直接透過陣列的 forEach 取出每一個元素。

var arr = ['Jerry', 'Anna'];

arr.forEach(item => console.log(item));

forEach 是 FP 操作陣列的基本方法钦勘,我們可以用這個方法來實作下面三個我們今天要講的重點分別為 map, filter, concatAll陋葡。

Map

試著把 newCourseList 每個元素的 { id, title } 塞到新的陣列 idAndTitlePairs

var newCourseList = [
    {
        "id": 511021,
        "title": "React for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 5
    },
    {
        "id": 511022,
        "title": "Vue2 for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 5
    },
    {
        "id": 511023,
        "title": "Angular2 for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 5
    },
    {
        "id": 511024,
        "title": "Webpack for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 4
    }
], idAndTitle = [];

newCourseList.forEach((course) => {
    idAndTitle.push({ id: course.id, title: course.title });
});

雖然我們成功的把 newCourseList 轉(zhuǎn)成 idAndTitlePairs,但這樣的寫法還是顯得有點太複雜了彻采,我們可以用更抽象化的方式來完成腐缤。

上面我們練習到 newCourseList 轉(zhuǎn)換成一個新的陣列 idAndTitlePairs,這個轉(zhuǎn)換的過程其實就是兩件事

  • 遍歷 newCourseList 所有的元素
  • 把每個元素的預期值給到新的陣列

把這個過程抽象化成一個方法 map肛响,以下是簡化的基本思路:

  1. 我們會讓每個 陣列 都有一個 map 方法
  2. 這個方法會讓使用者自訂傳入一個 callback function
  3. 這個 callback function 會回傳使用者預期的元素

雖然 ES5 之后原生的 JavaScript 陣列有 map 方法了岭粤,但希望讀者自我實做一次,能幫助理解特笋。

// 我們希望每一個陣列都有 map 這個方法剃浇,所以我們在 Array.prototype 擴充 map function
Array.prototype.map = function(callback) {
  var result = []; // map 最后一定會返回一個新陣列,所以我們先宣告一個新陣列

  this.forEach(function(element, index) {
      // this 就是呼叫 map 的陣列
      result.push(callback(element, index));
      // 執(zhí)行使用者定義的 callback, callback 會回傳使用者預期的元素虎囚,所以我們把它 push 進新陣列
  })

  return result;
}

這裡用到了 JavaScript 的 prototype chain 以及 this 等觀念角塑,可以看此影片了解!

到這裡我們就實作完成 map 的方法了淘讥,讓我們來試試這個方法吧圃伶!

var idAndTitle = newCourseList
                 .map((course) => {
                     return { id: course.id, title: course.title };
                 });

可以看到我們的程式碼更加的簡潔!

Filter

如果我們希望過濾一個陣列适揉,留下陣列中我們想要的元素留攒,并產(chǎn)生一個新的陣列,要怎麼做呢嫉嘀?
先讓我們用 forEach 完成炼邀!

讓我們過濾出 rating 值是 5 的元素

var ratingIsFive = [];

newCourseList.forEach((course) => {
    if(course.rating === 5) {
        ratingIsFive.push(course);
    }
});

同樣的我們試著來簡化這個過程,首先在這個轉(zhuǎn)換的過程中剪侮,我們做了兩件事:

  1. 遍歷 newCourseList 中的所有元素
  2. 判斷元素是否符合條件拭宁,符合則加到新的陣列中
Array.prototype.filter = function(callback) {
    var result = [];
    this.forEach((item, index) => {
        if(callback(item, index))
            result.push(item);
    });
    return result;
}

試試這個方法

var ratingIsFive = newCourseList
                   .filter((course) => course.rating === 5);

會發(fā)現(xiàn)我們的程式碼又變簡單了,接著我們試著把 filter, map 串起來瓣俯。

如果我想要取出所有 rating 是 5 的所有 course title

var ratingIsFive = newCourseList
                   .filter((course) => course.rating === 5)
                   .map(course => course.title);

ConcatAll

有時候我們會遇到組出一個二維陣列杰标,但我們希望陣列是一維的,問題如下:

假如我們要取出 courseLists 中所有 rating 為 5 的課程彩匕,這時可能就會用到兩個 forEach

var user = {
  id: 888,
  name: 'JerryHong',
  courseLists: [{
    "name": "My Courses",
    "courses": [{
      "id": 511019,
      "title": "React for Beginners",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ id: 1, name: "JavaScript" }],
      "rating": 5
    }, {
      "id": 511020,
      "title": "Front-End automat workflow",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ "id": 2, "name": "gulp" }, { "id": 3, "name": "webpack" }],
      "rating": 4
    }]
  }, {
    "name": "New Release",
    "courses": [{
      "id": 511022,
      "title": "Vue2 for Beginners",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ id: 1, name: "JavaScript" }],
      "rating": 5
    }, {
      "id": 511023,
      "title": "Angular2 for Beginners",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ id: 1, name: "JavaScript" }],
      "rating": 4
    }]
  }]
};

var allCourseIds = [];

user.courseLists.forEach(list => {
  list.courses
    .filter(item => item.rating === 5)
    .forEach(item => {
      allCourseIds.push(item)
    })
})

可以看到上面的程式碼腔剂,我們用了較為低階的操作來解決這個問題,我們剛剛已經(jīng)試著用抽象化的方式實作了 map 跟 filter驼仪,那我們同樣也能夠定義一個方法用來 攤平二維陣列掸犬。

讓我們來加入一個 concatAll 方法來簡化這段程式碼吧!
concatAll 要做的事情很簡單绪爸,就是把一個二維陣列轉(zhuǎn)成一維湾碎。

Array.prototype.concatAll = function() {
  var result = [];

  // 用 apply 完成
  this.forEach((array) => {
    result.push.apply(result, array);
  });

  // 用兩個 forEach 完成
  // this.forEach((array) => {
  //   array.forEach(item => {
  //     result.push(item)
  //   })
  // });

  // 用 ES6 spread 完成
  // this.forEach((array) => {
  //   result.push(...array);
  // })

  return result;
};

同樣的我們用前面定要好的 courseLists 來試試 concatAll 吧!

var allCourseIds = user.courseLists.map(list => {
    return list.courses.filter(course => course.rating === 5)
}).concatAll()

這邊出一個比較難的題目奠货,大家可以想想看要怎麼解

var courseLists = [{
  "name": "My Courses",
  "courses": [{
    "id": 511019,
    "title": "React for Beginners",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/tech"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/tech"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/tech"
    }],
    "tags": [{
      id: 1,
      name: "JavaScript"
    }],
    "rating": 5
  }, {
    "id": 511020,
    "title": "Front-End automat workflow",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/arch"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/arch"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/arch"
    }],
    "tags": [{
      "id": 2,
      "name": "gulp"
    }, {
      "id": 3,
      "name": "webpack"
    }],
    "rating": 5
  }]
}, {
  "name": "New Release",
  "courses": [{
    "id": 511022,
    "title": "Vue2 for Beginners",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/nature"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/nature"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/nature"
    }],
    "tags": [{
      id: 1,
      name: "JavaScript"
    }],
    "rating": 5
  }, {
    "id": 511023,
    "title": "Angular2 for Beginners",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/people"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/people"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/people"
    }],
    "tags": [{
      id: 1,
      name: "JavaScript"
    }],
    "rating": 5
  }]
}];

/* 
var result = courseList
不得直接使用索引 covers[0]介褥,請用 concatAll, map, filter, forEach 完成
result 結(jié)果為 [
    {
      id: 511019,
      title: "React for Beginners",
      cover: "http://placeimg.com/150/200/tech"
    }, {
      id: 511020,
      title: "Front-End automat workflow",
      cover: "http://placeimg.com/150/200/arch"
    }, {
      id: 511022,
      title: "Vue2 for Beginners",
      cover: "http://placeimg.com/150/200/nature"
    }, {
      id: 511023,
      title: "Angular2 for Beginners",
      cover: "http://placeimg.com/150/200/people"
    },
 ]
*/

練習連結(jié): JSBin | JSFiddle

這題有點難,大家可以想想看递惋,我把答案寫在這裡了柔滔!

如果大家還想做更多的練習可以到這個連結(jié):http://reactivex.io/learnrx/

這個連結(jié)是 Jafar 大神為他的 RxJS workshop 所做的練習網(wǎng)站!

今日小結(jié)

今天講了 FP 操作陣列的三個通用函式 forEach, map, filter萍虽,以及我們自己定義的一個方法叫 concatAll廊遍。這幾天我們把學習 RxJS 的前置觀念跟知識基本上都講完了,明天我們就開始進入 RxJS 的重點核心 Observable 蘿贩挣!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子王财,更是在濱河造成了極大的恐慌卵迂,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绒净,死亡現(xiàn)場離奇詭異见咒,居然都是意外死亡,警方通過查閱死者的電腦和手機挂疆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門改览,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缤言,你說我怎么就攤上這事宝当。” “怎么了胆萧?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵庆揩,是天一觀的道長。 經(jīng)常有香客問我跌穗,道長订晌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任蚌吸,我火速辦了婚禮锈拨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘羹唠。我一直安慰自己奕枢,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布肉迫。 她就那樣靜靜地躺著验辞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪喊衫。 梳的紋絲不亂的頭發(fā)上跌造,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音族购,去河邊找鬼壳贪。 笑死,一個胖子當著我的面吹牛寝杖,可吹牛的內(nèi)容都是我干的违施。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼瑟幕,長吁一口氣:“原來是場噩夢啊……” “哼磕蒲!你這毒婦竟也來了留潦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤辣往,失蹤者是張志新(化名)和其女友劉穎兔院,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體站削,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡坊萝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了许起。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十偶。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖园细,靈堂內(nèi)的尸體忽然破棺而出惦积,到底是詐尸還是另有隱情,我是刑警寧澤珊肃,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布荣刑,位于F島的核電站,受9級特大地震影響伦乔,放射性物質(zhì)發(fā)生泄漏厉亏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一烈和、第九天 我趴在偏房一處隱蔽的房頂上張望爱只。 院中可真熱鬧,春花似錦招刹、人聲如沸恬试。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽训柴。三九已至,卻和暖如春妇拯,著一層夾襖步出監(jiān)牢的瞬間幻馁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工越锈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仗嗦,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓甘凭,卻偏偏與公主長得像稀拐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丹弱,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容