Devahoy Logo
PublishedAt

JavaScript

[JavaScript] Sort ข้อมูลใน Object ทำยังไง?

[JavaScript] Sort ข้อมูลใน Object ทำยังไง?

ก่อนหน้านี้เราพูดถึง JavaScript sort() เบื้องต้นกันไปแล้ว วันนี้จะมาลองใช้งาน sort() ในระดับลึกขึ้นนั่นก็คือ การ sort ข้อมูลในรูปแบบที่เป็น Object ใน Array กันนะครับ

หลายๆ คนอาจจะเคยใช้งาน orderBy หรือ sortBy ของ Lodash หรือ Underscore มา แต่วันนี้มาลองทำ sortBy ของเราเองแบบไม่ต้องใช้ third party library

JavaScript sort ข้อมูลตัวเลข ไม่ถูกต้อง?

สมมติ เรามีข้อมูลเริ่มต้น เป็น Array ที่มีข้อมูล Object ดังนี้

1
const items = [
2
{ name: 'Edward', value: 21 },
3
{ name: 'Sharpe', value: 37 },
4
{ name: 'And', value: 45 },
5
{ name: 'The', value: -12 },
6
{ name: 'Magnetic', value: 13 },
7
{ name: 'Zeros', value: 37 }
8
]

Sorting

ถ้าเราอยาก sort จากข้อมูล Object ใน Array ทำได้ยังไง?

เรียงค่า value

ถ้าอยากให้เรียงจากค่า value ซึ่ง value เป็น number เราสามารถ sort ได้ง่ายๆ แบบนี้

1
items.sort((a, b) => a.value - b.value)

เรียงค่า name

ถ้าอยาก sort ค่า name ซึ่งเป็น string ทำได้โดย เริ่มจาก เราเช็คให้เป็น lowercase หรือ uppercase ก่อน จากนั้นก็เปรียบเทียบค่า ถ้า a น้อย กว่า b ก็ return -1 a มากกว่า return 1 เท่ากัน ก็ 0 แบบนี้

1
items.sort((a, b) => {
2
const nameA = a.name.toUpperCase() // ignore upper and lowercase
3
const nameB = b.name.toUpperCase() // ignore upper and lowercase
4
5
if (nameA < nameB) {
6
return -1
7
}
8
if (nameA > nameB) {
9
return 1
10
}
11
12
// names must be equal
13
return 0
14
})

ลอง reuse function ให้รองรับการ sort แบบน้อยไปมาก หรือมากไปน้อยได้ โดยใช้ ASC หรือ DESC แบบนี้

1
const createCompareFn = (direction = 'ASC') => {
2
return (a, b) => {
3
if (direction === 'ASC') {
4
return a.value - b.value
5
} else if (direction === 'DESC') {
6
return b.value - a.value
7
}
8
}
9
}

ผมสร้าง function ที่รับ items และ direction จากนั้น ก็ return function ที่เป็น compareFn (มี argument คือ a, b) เวลาเราใช้งานก็เรียกแบบนี้

1
const sortByDesc = createCompareFn('DESC')
2
const sortBy = createCompareFn()
3
4
// เวชา sort ก็เรียก
5
items.sort(sortByDesc)
6
items.sort(sortBy)

ทีนี้ ข้อจำกัดของมัน คือ function compare เรา sort แค่ number ไม่ได้ sort string จะ sort string เราก็ต้องเปลี่ยน function อีกเป็นแบบนี้

1
const createCompareFn = (direction = 'ASC') => {
2
return (a, b) => {
3
const nameA = a.name.toUpperCase() // ignore upper and lowercase
4
const nameB = b.name.toUpperCase() // ignore upper and lowercase
5
if (nameA < nameB) {
6
return -1
7
}
8
if (nameA > nameB) {
9
return 1
10
}
11
12
return 0
13
}
14
}

การใช้งาน ก็เหมือนเดิม

1
const sortByName = createCompareFn()
2
3
items.sort(sortByName)

ดูแล้ว ถ้าเราอยาก sort ค่าอื่นๆ เราก็ต้องมา สร้าง function compare ใหม่ ทุกครั้ง ก็รู้สึกว่ามันยังไม่ตอบโจทย์ ทำไมเราไม่ทำ function ให้มันรับค่า key ที่เราต้องการซะเลย

สร้าง function ให้รับ key อะไรก็ได้

ก็คือ เราอยากให้มัน sort ด้วย key อะไรใน object ก็แค่ใส่ค่า key เข้าไป ข้างในผมก็ เช็คก่อนว่ามันเป็น string หรือ number แบบนี้ (logic compare ก็แบบเดียวกันกับก่อนหน้า)

1
const createCompareFn = (key, direction = 'ASC') => {
2
const isString = typeof a[key] === 'string' && typeof b[key] === 'string'
3
4
if (isString) {
5
// logic compare string
6
} else {
7
// logic compare number
8
}
9
}

ทีนี้ตรงเงื่อนไข sort น้อยไปมาก หรือมากไปน้อย ถ้าเรามาเช็ค แบบนี้ มันดูจะไม่สวย และซับซ้อนกับ check string

1
const nameA = a.name.toUpperCase() // ignore upper and lowercase
2
const nameB = b.name.toUpperCase() // ignore upper and lowercase
3
4
if (direction === 'ASC') {
5
if (nameA < nameB) {
6
return -1
7
}
8
if (nameA > nameB) {
9
return 1
10
}
11
return 0
12
} else {
13
if (nameA < nameB) {
14
return 1
15
}
16
if (nameA > nameB) {
17
return -1
18
}
19
return 0
20
}
21
``
22
23
เราใช้เป็น ค่าๆ นึงขึ้นมา เพื่อให้เป็น เครื่องหมายตรงข้าม นั่นก็คือ 1 และ -1 แบบนี้
24
25
```js
26
const directionValue = direction === 'ASC' ? 1 : -1

ผลลัพธ์ function ที่ได้ ก็ได้แบบนี้

1
const createCompareFn = (key, direction = 'ASC') => {
2
return (a, b) => {
3
const isString = typeof a[key] === 'string' && typeof b[key] === 'string'
4
const directionValue = direction === 'ASC' ? 1 : -1
5
6
if (isString) {
7
const A = a[key].toUpperCase()
8
const B = b[key].toUpperCase()
9
10
if (A < B) return -1 * directionValue
11
if (A > B) return 1 * directionValue
12
return 0
13
} else {
14
return (a[key] - b[key]) * directionValue
15
}
16
}
17
}

ตัวอย่าง ผมเพิ่ม parameter key เพื่อให้ user เลือกได้ว่าจะ sort ด้วย property อะไรของ Object จากนั้น ใน compareFn เราก็ เข้าถึงได้จาก a[key] เราจะรู้ว่าเป็น string หรือ number และก็ reuse function ก่อนหน้ามาใช้

ทีนี้ ส่วน directionValue ก็เป็นสิ่งที่เอาไว้บอก ถ้า asc ผมก็ให้มัน 1 และ desc = -1 เพื่อให้มันมีความหมายตรงกันข้ามกัน

ทีนี้ในการเช็ค compare number return 1, -1 และ 0 มันก็จะสลับกันเป็น -1, 1, 0 นั่นเอง

เวลาใช้งานก็แค่ง่ายๆ แบบนี้

1
const sortByName = createCompareFn('name')
2
const sortByNameDesc = createCompareFn('name', 'DESC')
3
const sortByValue = createCompareFn('value')
4
5
items.sort(sortByName)
6
items.sort(sortByNameDesc)

จบแล้ว หวังว่าจะเห็นแนวทาง และลองนำไปใช้กันดูนะครับ

ซึ่ง function ที่ผมยกตัวอย่างมาก็เป็นเพียงแค่ function ง่ายๆ ไม่ได้ซับซ้อน ถ้าเราจะใช้งานจริงๆ เราอาจจะต้องเช็คว่า property key มีค่ามั้ย และค่าเป็นอะไร เพื่อจะ compare ถูก หรือรองรับ nest object มั้ย? หรือค่า direction หรือ key เป็น case sensitive หรือ case insensitive

ลองไปฝึกทำ ฝึกเล่น และปรับแก้ พัฒนาเพิ่มกันดูนะครับ

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts