比對兩個 array 的 element

2021-03-14 · 3 min read

問題描述

工作上遇到的問題,狀況是使用者有個管理顧客資料的頁面,可以在那裡編輯顧客的 tag,所以在更新資料的時候要比對新的 tag list 和舊的 tag list 兩者內容,以傳遞正確的資訊回去給資料庫。

新的 tag list 是一個 array,這個 array 只有單純的 tag name,像是這樣:

["aaa", "bbb", "ccc", "ddd"];

另外有一個前一狀態(舊的)tag list ,這個 list 記錄著更多訊息,包括每個 tag 的 id、名稱以及數量等等,並以 JSON 格式儲存:

"[ {"id":1,"name":"aaa","count":3}, {"id":2,"name":"bbb","count":1}, {"id":3,"name":"ccc","count":5} ]"

原本的編輯功能沒有做好,使用者編輯完 tag 後按下 確認 後會更新 tag list,但是更新後的 tag list 把原本 tag count 都記錄成 1 了:

handleAddTag = (id) => (e) => { const currentTagNameList = this.state.tagData; // 把每個 tag 弄成 object,並把頭尾空白去掉 let newTagDataList = currentTagNameList.map((t) => { return { name: t.trim() }; }); // 把更新後的 new tag list 傳遞出去 this.props.editProfile(id, newTagDataList); };

這個錯誤的原因就是沒有把舊 tag list 的資料給拿來處理,以下只探討怎麼確認新舊資料是否吻合,並將舊的詳細資料塞進 newTagDataList。

解法

最先想到的解法

const currentTagNameList = this.state.tagData; // 把舊資料拿出來並 parse JSON const prevTagDataList = JSON.parse(this.state.prevTagData); // 做成一個新的 list 但只有 tag 的名字 const prevTagNameList = prevTagDataList.map((tag) => tag.name); // map 的時候新舊兩相對照, // 如果在新的 list 裡發現和舊的 tag 名字一樣,那就把舊的詳細資料塞進來 let newTagDataList = currentTagNameList.map((t) => { if (prevTagNameList.includes(t)) { return prevTagDataList.find((tag) => tag.name === t); } return { name: t.trim() }; });

這樣是可以把資料塞進來,但效率就是差,因為 map 裡面還塞了 includes,而 includes 裡又有 find,所以是 O(n^3)。

優化解

最近在看演算法的內容,學到了如果排序不是重點的話,用 object 來處理資料會是比較好的做法,所以就從這個方向來改。

const currentTagNameList = this.state.tagData; // 一樣拿舊資料出來 const prevTagDataList = JSON.parse(this.state.prevTagData); // 建立一個可以比對舊 tag list 的 tag name 和 index 的 Map let prevTagNameIndexMap = {}; prevTagDataList.forEach( (tag, index) => (prevTagNameIndexMap[tag.name] = index) ); let newTagDataList = currentTagNameList.map((t) => { const tagName = t.trim(); // 如果新的 tag list 裡的 tag 可以在舊的 list 找到,那就把舊的資料塞進來 if (tagName in prevTagNameIndexMap) { return prevTagDataList[prevTagNameIndexMap[tagName]]; } return { name: tagName }; });

這邊優化的重點在於將 tag name 當作 key 而 index 當作 value 做出一個可以舊資料的 Map,所以要從舊資料找特定 tag 時可以直接用 index 去 access,這樣優化過後就變成 O(n)。

建立技術 Blog 前的比較height: 100% 在 Safari 失效