DEV Community

Ta for tamemo

Posted on • Edited on • Originally published at tamemo.com

3

[Dart] รวมคำสั่งดีๆ เอาไว้ใช้กับ List

ในบทความที่เรา เราแนะนำการใช้งาน 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]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

หมวดการคัดลอก/เพิ่ม/ลบ

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!
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

แต่ใน 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]
Enter fullscreen mode Exit fullscreen mode

หมวด 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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

ข้อควรระวังคือ 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

เช่นเดียวกับ 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
);
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

แต่สังเกตดูว่ามันจะเท่ากับการเขียนลูป โดย 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
Enter fullscreen mode Exit fullscreen mode

แต่ก็อย่างที่เห็นค่ามันไม่สวยเลย! ในเคสนี้เราสามารถเปลี่ยนจาก 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
Enter fullscreen mode Exit fullscreen mode

ลองมาดูตัวอย่างกันอีก

เช่นเราต้องการหาค่าที่มากที่สุดในลิสต์เราอาจจะเขียน combine แบบนี้

import 'dart:math';

List<int> list = [1, 2, 3, 4, 5];
int maxNum = list.reduce((x, y) => max(x, y));
Enter fullscreen mode Exit fullscreen mode

และไม่จำเป็นจะต้องใช้กับลิสต์ของ int เท่านั้น สามารถเอาไปแอพพลายใช้งานกับ String ก็ยังได้ เช่นต้องการจะเชื่อมสตริง (concat) เข้าด้วยกันด้วย - ก็เขียนแบบนี้ได้

List<String> list = ['A', 'B', 'C'];
String str = list.reduce((x, y) => '$x-$y');
// str is "A-B-C"
Enter fullscreen mode Exit fullscreen mode

หมวด Utility

length, isEmpty

🍐 Scala

length ใช้เพื่อเช็กว่าตอนนี้ในลิสต์มี item อยู่กี่ตัว ส่วน isEmpty ใช้เช็กว่าตอนนี้ item ในลิสต์ไม่มีเลยใช่หรือไม่

List<int> list = [1, 2, 3, 4, 5];
print(list.length);     // 5
print(list.isEmpty);    // false
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

sort()

😱 Mutable | 🥚 void

ใช้สำหรับเรียงข้อมูลในลิสต์

List<int> list = [4, 2, 1, 5, 3];
list.sort();

// list is [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

ในกรณีที่ต้องการเรียงแบบอื่นเช่น 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);
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

แต่ถ้าอยากให้ผลการสลับออกมาเหมือนเดิมเราสามารถใส่ 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!
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Sentry mobile image

Improving mobile performance, from slow screens to app start time

Based on our experience working with thousands of mobile developer teams, we developed a mobile monitoring maturity curve.

Read more