ในบทความที่เรา เราแนะนำการใช้งาน List ไปแล้ว ในบทความนี้จะมาพูดกันต่อถึงเมธอดที่ควรรู้สำหรับการใช้งาน List ให้ดี/ง่ายยิ่งขึ้นกัน
แต่ก่อนอื่น เราจะอธิบายคำศัพท์ที่จะใช้ในบทความนี้ซะก่อน
Immutable vs Mutable
เมธอดของ List แต่ละตัวมีรูปแบบการทำงานที่ไม่เหมือนกัน โดยจะแบ่งเป็น
- Immutable - คำสั่งประเภทนี้จะไม่เปลี่ยนแปลง List (แต่ให้ผลลัพธ์ผ่านการ return)
- Mutable - คำสั่งประเภทนี้จะเปลี่ยนค่าของ List ตรงๆ
ซึ่งในบทความนี้จะใช้สัญลักษณ์แบบนี้ 😤 Immutable | 😱 Mutable
Result Type
เมธอดแต่ละตัวมีการตอบผลลัพธ์ (return) กลับมาไม่เหมือนกัน แต่แบ่งได้เป็นประมาณนี้
- List - ตอบกลับมาเป็น List เหมือนเดิม
- Iterable - ตอบกลับเป็น Iterable (เป็นโครงสร้างแบบ abstract คือสามารถเอามาวนลูปเพื่อขอค่าทีละตัวได้ ซึ่งส่วนใหญ่ทำงานแบบ lazy นั่นคือถ้ายังไม่มีการเรียกใช้จะไม่ทำงาน ต่างจาก List ที่ทำงานทันที)
-
Scala - ตอบกลับมาเป็นค่าชนิดอื่นเลย เช่น
int
,bool
,String
- void - ไม่ตอบค่าอะไรกลับมาเลย
ซึ่งในบทความนี้จะใช้สัญลักษณ์แบบนี้ 🍇 List | 🍒 Iterable | 🍐 Scala | 🥚 void
หมวดการสร้าง
generate()
😤 Immutable | 🍇 List
ใช้สำหรับการสร้างลิสต์ขึ้นมาโดยกำหนดจำนวน item ที่ต้องการ แล้วเราต้องกำหนด function สำหรับสร้าง item ขึ้นมา โดยสิ่งที่เราจะได้คือ index
List<int> list = List<int>.generate(10, (i) => i + 1);
// List [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
List Comprehension
😤 Immutable | 🍇 List
ซึ่งแทนที่จะใช้ .generate()
เราสามารถสร้างลิสต์โดยใช้การวน for
แทนก็ได้
List<int> list = [ for(var i=0; i<10; i++) i + 1 ];
// List [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
หมวดการคัดลอก/เพิ่ม/ลบ
form()
, of()
😤 Immutable | 🍇 List
เป็น 2 คำสั่งที่ใช้สร้างลิสต์จากลิสต์อีกตัวหนึ่ง ซึ่งมีข้อแตกต่างกันเล็กน้อยนั่นคือถ้าเราสั่งด้วย from
ผลที่ได้จะเป็นไทป์แบบ dynamic
แต่ถ้าใช้ of
ผลที่ได้จะได้ไทป์ของลิสต์ต้นฉบับมาด้วย
var list1 = new List.from(<int>[1, 2, 3]);
// list1 is List<dynamic>
var list2 = new List.of(<int>[1, 2, 3]);
// list2 is List<int>
List<String> list3 = new List.from(<int>[1, 2, 3]);
// Ok until runtime.
List<String> list4 = new List.of(<int>[1, 2, 3]);
// Compile Time Error!
add()
, addAll()
😱 Mutable | 🥚 void
ใช้เพื่อเพิ่ม item ลงไปในลิสต์ โดยใช้ add
สำหรับเพิ่ม item แบบทีละตัวและใช้ addAll
สำหรับการเพิ่มหลายๆ ตัวในรูปแบบของ List หรือ Iterable
List<int> list1 = [1, 2, 3];
list1.add(4);
// list1 is [1, 2, 3, 4]
List<int> list2 = [1, 2, 3];
list2.addAll([4, 5]);
// list2 is [1, 2, 3, 4, 5]
followedBy()
😤 Immutable | 🍒 Iterable
เป็นการสร้างลิสต์ตัวใหม่โดยเอาลิสต์อีกตัวมาต่อกัน แต่จะไม่ใช่การ add
หรือ addAll
ที่เพิ่ม item เข้าไปในลิสต์ แต่เป็นการสร้างลิสต์ตัวใหม่ออกมาแทน
List<int> list1 = [1, 2, 3];
Iterable<int> list2 = list1.followedBy([4, 5]);
//list1 is List [1, 2, 3]
//list2 is Iterable (1, 2, 3, 4, 5)
แต่ใน Dart การต่อ List สามารถใช้การ +
หรือ ...
(spread operator) แทนได้
List<int> list1 = [1, 2, 3];
List<int> list2 = list1 + [4, 5];
//list1 is [1, 2, 3]
//list2 is [1, 2, 3, 4, 5]
List<int> list3 = [1, 2, 3];
List<int> list4 = [...list1, ...[4, 5], 6];
//list3: [1, 2, 3]
//list4: [1, 2, 3, 4, 5, 6]
หมวด map/filter/reduce
forEach()
😤 Immutable | 🥚 void
ใช้ในการวนลูป item ทุกตัวในลิสต์ในรูปแบบการสร้างฟังก์ชันขึ้นมา (ซึ่งมาผลเทียบกับการวน for แบบธรรมดานั่นแหละ)
var list = [1, 2, 3];
list.forEach((item) => print(item));
// เขียนแบบใช้ลูป
var list = [1, 2, 3];
for(var item in list){
print(item);
}
map()
😤 Immutable| 🍒 Iterable
ใช้ในการแปลง item ทุกค่าในลิสต์ให้กลายเป็นอีกค่าหนึ่งด้วยวิธีการเดียวกันทั้งหมด โดยสร้างฟังก์ชันขึ้นมา
เช่น ในตัวอย่างข้างล่าง เราต้องการคูณเลขทุกตัวในลิสต์ด้วย 10 ก็ให้สร้าง (x) => x * 10
ฟังก์ชันที่รับตัวเลขเข้าไปหนึ่งตัวแล้วตอบค่านั้นเอาไปคูณด้วย 10 กลับมา
List<int> num1 = [1, 2, 3, 4, 5];
Iterable<int> num2 = num1.map((x) => x * 10);
List<int> num3 = num1.map((x) => x * 10).toList();
// num2 is Iterable (10, 20, 30, 40, 50)
// num3 is List [10, 20, 30, 40, 50]
// เขียนแบบใช้ลูป
List<int> num2 = <int>[];
for(var x in num1){
num2.add(x * 10);
}
ข้อควรระวังคือ map
รีเทิร์นค่ากลับมาเป็น Iterable
ไม่ใช่ List
ดังนั้นถ้าเราต้องการผลลัพธ์เป็นลิสต์เหมือนเดิมจะต้องสั่ง toList()
อีกที
where()
(หรือ filter)
😤 Immutable | 🍒 Iterable
ฟังก์ชัน where
หรือในภาษาอื่นมักจะเรียกว่า filter มีไว้ทำการเลือกเฉพาะ item ที่ตรงกับเงื่อนไข โดยจะต้องสร้างฟังก์ชันประเภท predicate หรือฟังก์ชันที่ตอบค่าเป็น bool
(true
= เลือกไอเทมนี้, false
= ไม่เอาไอเทมนี้)
เช่นหากเราต้องการเลือกเฉพาะตัวเลขที่เป็นเลขคู่ ก็ต้องสร้าง predicate function แบบนี้ (x) => x % 2 == 0
List<int> num1 = [1, 2, 3, 4, 5];
Iterable<int> num2 = num1.where((x) => x % 2 == 0);
List<int> num3 = num1.where((x) => x % 2 == 0).toList();
// num2 is Iterable (2, 4)
// num3 is List [2, 4]
// เขียนแบบใช้ลูป
var num1 = [1, 2, 3, 4, 5];
var num2 = <int>[];
for(var x in num1){
if(x % 2 == 0){
num2.add(x);
}
}
เช่นเดียวกับ map
คือ where
รีเทิร์นค่ากลับเป็น Iterable
ส่วนกลับของ
where
คือremoveWhere
ซึ่งจะลบตัวที่ตรงกับ predicate ทิ้งออกไปแทนข้อควรระวังคือ
removeWhere
เป็น Mutable นะ!
firstWhere()
และ singleWhere()
😤 Immutable | 🍐 Scala
นอกจากนี้ ภาษาDartยังมีฟังก์ชันสำหรับเลือกค่าที่ตรงเงื่อนไขอีก 2 ตัวคือ firstWhere
และ singleWhere
ซึ่งจะตอบตัวเลขที่ตรงเงื่อนไขกลับมาแค่ตัวเดียวเท่านั้น
ข้อแตกต่างระหว่าง firstWhere และ singleWhere คือการใช้ singleWhere
จะต้องมีค่านั้นเพียงตัวเดียวเท่านั้น ถ้ามีตัวที่ตรงกับเงื่อนไขหลายตัวจะเจอ Error: Bad state: Too many elements
List<int> list = [1, 2, 3, 4, 5];
int num = num1.firstWhere(
(x) => x % 2 == 0,
orElse: () => null
);
List<int> list = [1, 2, 3, 4, 5];
int num = num1.singleWhere(
(x) => x % 2 == 0,
orElse: () => null
);
reduce()
, fold()
😤 Immutable | 🍐 Scala
reduce จะใช้ในกรณีที่เราต้องการรวม item ทุกตัวในลิสต์ให้ออกมาเป็นค่าใหม่ค่าเดียวเท่านั้นด้วยวิธีการอะไรบางอย่าง ซึ่งเราต้องเขียนขึ้นมาด้วยฟังก์ชันที่เรียกว่า combine
เช่นหากเราต้องการเอา item ทุกตัวในลิสต์มา +
กัน ให้เขียน combine function ว่า (x, y) => x + y
นั่นคือหากเรามีตัวเลขสองตัว ให้เอาสองตัวนั้นมาบวกกัน
List<int> list = [1, 2, 3, 4, 5];
int num = list.reduce((x, y) => x + y);
// num is 1 + 2 + 3 + 4 + 5
// num is 15
// เขียนแบบใช้ลูป
int num = list.first;
for(var x in list.skip(1)){
num += x;
}
แต่สังเกตดูว่ามันจะเท่ากับการเขียนลูป โดย accumulator (หมายถึงตัวแปรที่เอาไว้สะสมค่า ในกรณีนี้คือ num) จะต้องเริ่มต้นที่ item ตัวแรกเป็นตัวตั้ง แต่ถ้าเราต้องการกำหนดค่าเริ่มต้นเอง เราสามารถเขียนแบบนี้ได้
List<int> list = [1, 2, 3, 4, 5];
int num = [100, ...list].reduce((x, y) => x + y);
// num is 100+ 1 + 2 + 3 + 4 + 5
แต่ก็อย่างที่เห็นค่ามันไม่สวยเลย! ในเคสนี้เราสามารถเปลี่ยนจาก reduce
ไปใช้ fold
แทนได้ แบบนี้
List<int> list = [1, 2, 3, 4, 5];
int num = list.fold(100, (x, y) => x + y);
// num is 100+ 1 + 2 + 3 + 4 + 5
ลองมาดูตัวอย่างกันอีก
เช่นเราต้องการหาค่าที่มากที่สุดในลิสต์เราอาจจะเขียน combine แบบนี้
import 'dart:math';
List<int> list = [1, 2, 3, 4, 5];
int maxNum = list.reduce((x, y) => max(x, y));
และไม่จำเป็นจะต้องใช้กับลิสต์ของ int
เท่านั้น สามารถเอาไปแอพพลายใช้งานกับ String
ก็ยังได้ เช่นต้องการจะเชื่อมสตริง (concat) เข้าด้วยกันด้วย -
ก็เขียนแบบนี้ได้
List<String> list = ['A', 'B', 'C'];
String str = list.reduce((x, y) => '$x-$y');
// str is "A-B-C"
หมวด Utility
length
, isEmpty
🍐 Scala
length
ใช้เพื่อเช็กว่าตอนนี้ในลิสต์มี item อยู่กี่ตัว ส่วน isEmpty
ใช้เช็กว่าตอนนี้ item ในลิสต์ไม่มีเลยใช่หรือไม่
List<int> list = [1, 2, 3, 4, 5];
print(list.length); // 5
print(list.isEmpty); // false
sublist()
, getRange()
😤 Immutable |
sublist()
= 🍇 List, getRange()
= 🍒 Iterable
ใช้สำหรับตัดลิสต์ย่อยออกมาโดยเราต้องกำหนด index ที่เริ่มและจบ (start, end) ข้อควรระวังคือ end จะไม่ถูกรวมอยู่ในคำตอบด้วย
List<int> list = [1, 2, 3, 4, 5];
list.sublist(1, 3); // [2, 3]
list.getRange(1, 3); // (2, 3)
first
, last
🍐 Scala
ใช้ขอ item ตัวแรกสุดและตัวท้ายสุดของลิสต์ มีค่าเท่ากับ list[0]
และ list[list.length - 1]
List<int> list = [1, 2, 3, 4, 5];
print(list.first); // 1
print(list.last); // 5
take()
, skip()
, takeWhile()
, skipWhile()
😤 Immutable | 🍒 Iterable
คล้ายกับ getRange
แต่เป็นการเลือก/ข้ามจาก head หรือหัวแถวของลิสต์แทน
สำหรับ takeWhile
กับ skipWhile
จะเป็นการสร้างฟังก์ชันเงื่อนไขขึ้นมาเช็กแทน
List<int> list = [1, 2, 3, 4, 5];
li.take(2) // (1, 2)
li.skip(2) // (3, 4, 5)
li.takeWhile((x) => x < 3) // (1, 2)
li.skipWhile((x) => x < 3) // (3, 4, 5)
contains()
, any()
, every()
🍐 Scala
ใช้สำหรับหาว่ามี item ที่ต้องการหรือไม่
สำหรับ any
(ข้อแค่ตรงเงื่อนไขอย่างน้อย 1 ตัว) และ every
(ไอเทมทุกตัวในลิสต์จะต้องตรงเงื่อนไขทั้งหมด) จะต้องสร้างฟังก์ชัน test ขึ้นมา
List<int> list = [1, 2, 3, 4, 5];
list.contains(3); // true
list.any((x) => x % 2 == 0); // true
list.every((x) => x % 2 == 0); // false
sort()
😱 Mutable | 🥚 void
ใช้สำหรับเรียงข้อมูลในลิสต์
List<int> list = [4, 2, 1, 5, 3];
list.sort();
// list is [1, 2, 3, 4, 5]
ในกรณีที่ต้องการเรียงแบบอื่นเช่น descending list หรือลิสต์ที่เรียงจาก มาก -> น้อย แทนหรือต้องการ sort ลิสต์ของ object
ที่ไม่ใช่ Primitive Type เราจะต้องสร้างฟังก์ชัน comparator ขึ้นมา โดยให้ยึดหลักการว่า ตัวแรก-ตัวที่สองเสมอ เช่น
List<int> list = [4, 2, 1, 5, 3];
list.sort((first, second) => -(first-second));
// นำมาลบกัน แต่ใส่ค่า - เอาไว้เพื่อให้เรียงจากมากไปน้อยแทน
class Score{
int point;
People(this.point);
}
var list = [Score(20), Score(5), Score(10))];
list.sort((first, second) => first.point - second.point);
shuffle()
😱 Mutable | 🥚 void
เมื่อมีการสั่งให้เรียงข้อมูลด้วย sort
แล้วก็ต้องมีคำสั่งที่เป็นส่วนกลับกัน นั่นคือ shuffle
หรือการสลับตำแหน่งลิสต์แบบ random (คล้ายสับกองการ์ดให้เรียงแบบมั่วๆ นั่นแหละ)
ซึ่งการสั่ง shuffle
แต่ละครั้งก็จะให้ผลออกมาไม่เหมือนกัน เพราะมัน random นะ
List<int> list = [1, 2, 3, 4, 5];
list.shuffle();
// list is [4, 2, 1, 5, 3]
list.shuffle();
// call again!
// list is [3, 4, 5, 2, 1]
แต่ถ้าอยากให้ผลการสลับออกมาเหมือนเดิมเราสามารถใส่ seed ซึ่งเป็นอ็อบเจก Random
ลงไปได้ โดยถ้า seed เป็นเลขเดิมจะได้ผลลัพธ์ออกมาเหมือนเดิม
import 'dart:math';
List<int> list = [1, 2, 3, 4, 5];
list.shuffle(Random(100));
// list is [2, 5, 1, 3, 4]
list.shuffle(Random(100));
// call again!
// list is [2, 5, 1, 3, 4] same result!
reversed
😤 Immutable | 🍒 Iterable
คำสั่งสำหรับสร้างลิสต์ตัวใหม่ที่กลับหัว item ทั้งหมดแทน
List<int> list = [1, 2, 3, 4, 5];
var re = list.reversed;
// list is List [1, 2, 3, 4, 5]
// re is Iterable [5, 4, 3, 2, 1]
Top comments (0)