Dart 102: โครงสร้างข้อมูลพื้นฐาน List และ Map

โครงสร้างข้อมูลหรือ Data Structure เป็นสิ่งที่ต้องมีในทุกภาษาโปรแกรม เพราะในการเขียนโปรแกรมจริงๆ การที่เรามีแค่ variable ไม่พอจะที่จัดการข้อมูลที่มากมายและซับซ้อนในแอพของเรา

Data Structure: List และ Map

โครงสร้างข้อมูลใน Dart มี 2 ประเภทหลักๆ คือ

  • List: เก็บข้อมูลแบบ linear, มีค่าเท่ากับ array ในบางภาษา
  • Map: เก็บข้อมูลแบบ key-value, มีค่าเท่ากับ dictionary หรือ hash-table ในบางภาษา

List

การสร้างลิสต์ของข้อมูลขึ้นมาทำได้โดยการประกาศตัวแปรชนิด List

List ใน Dart ไม่ใช่โครงสร้างแบบ fixed-length นั่นคือสามารถเพิ่ม/ลบ element ในลิสต์ได้ผ่านคำสั่ง เช่น add(), remove(), removeAt()

List myList = [10, 20, 30, 40];

ซึ่งจะได้ค่าดังนี้

print(myList);      // [10, 20, 30, 40]

print(myList[0]);   // 10
print(myList[1]);   // 20
print(myList[2]);   // 30
print(myList[3]);   // 40

แต่ก็ตามสไตล์ภาษามี type ทั่วๆ ไปคือการสร้าง List โดยที่ไม่ระบุอะไรนั้นมีความเสี่ยงอย่างมาก! เพราะ compiler จะไม่รู้ว่า element แต่ละตัวข้างในเป็นตัวแปรชนิดอะไร จึงช่วยเราเช็กค่าไม่ได้เลย --> ต้องไปเสี่ยงดวงตอน runtime กันเอง

List myList = [1, 'hi', true];

int x    = myList[0];  // ok!
String s = myList[1];  // ok!
bool b   = myList[2];  // ok!

int y    = myList[1];  // Runtime Error! TypeError: String is not a subtype of type int

ในภาษา Dart มีฟีเจอร์ที่เรียกว่า Type Interface หรือ Generic ให้ใช้ นั่นคือการส่ง type เข้าไปใน class หรือ function ได้โดยใช้สัญลักษณ์ ``

List<int> myList = [10, 20, 30, 40];  
// แบบนี้ ok!

var myList = <int>[10, 20, 30, 40];  
// แบบนี้ก็ ok!

//แต่...

List<int> myList = [10, 20, 'hi'];
//Error: A value of type 'String' can't be assigned to a variable of type 'int'.
//  List<int> myList = [10, 20, 'hi'];
//                               ^

การประกาศ List โดยไม่มี Generic จะถูกมองว่าเป็น List นั่นคืออนุญาตให้เก็บตัวแปรชนิดอะไรก็ได้

loop

การวนลูปสามารถทำได้ด้วยวิธีใช้ตัว counter แบบดั้งเดิม และนับขนาดของลิสต์ด้วย .length

List<int> numbers = [10, 20, 30, 40];
for(var i = 0; i < numbers.length; i++){
    print(numbers[i]);
}

หรือใช้ for-in

List<int> numbers = [10, 20, 30, 40];
for(var number in numbers){
    print(number);
}

การเพิ่มหรือลบ item ออกจาก List

การเพิ่ม item ลงใน List จะใช้คำสั่ง

  • add - ใช้สำหรับเพิ่ม item ที่ตำแหน่งท้ายสุดของลิสต์
  • insert - ใช้สำหรับเพิ่ม item ลงที่ตำแหน่งตรงกลางลิสต์
var list = ['A', 'B', 'C'];

list.add('D');          // เพิ่ม D เข้าไปท้ายสุดของลิสต์
print(list);            // [A, B, C, D]

list.insert(0, 'E');    // เพิ่ม E เข้าไปที่ตำแหน่งที่ 0
print(list);            // [E, A, B, C, D]

list.insert(3, 'F');    // เพิ่ม F เข้าไปที่ตำแหน่งที่ 3
print(list);            // [E, A, B, F, C, D]

list.insert(6, 'G');    // เพิ่ม G เข้าไปที่ตำแหน่งที่ 6
print(list);            // [E, A, B, F, C, D, G]

ในภาษา Dart ไม่มีคำสั่ง pop ซึ่งเอาไว้เพิ่ม item เข้าไปที่ตำแหน่งแรกสุดของลิสต์ แต่เราสามารถใช้การ insert ในตำแหน่งที่ 0 แทนได้

แต่ถ้า item ที่เราต้องการเพิ่มลงไปเป็น List (หรือ Iterable) เราจะใช้คำสั่ง addAll และ insertAll แทน

var list = ['A'];

list.addAll(['B', 'C']);
print(list);            // [A, B, C]

list.insertAll(2, ['D', 'E']);
print(list);            // [A, B, D, E, C]

ส่วนการลบ item ออกจากลิสต์จะใช้คำสั่ง remove

List<String> list;

list = ['A', 'B', 'C', 'D', 'E'];
list.remove('A');
print(list);    // [B, C, D, E]

list = ['A', 'B', 'C', 'D', 'E'];
list.removeAt(0);
print(list);    // [B, C, D, E]

list = ['A', 'B', 'C', 'D', 'E'];
list.removeRange(1,3);
print(list);    // [A, D, E]

var numbers = [1, 2, 3, 4, 5];
numbers.removeWhere( (number) => number % 2 == 0 );
print(numbers);    // [1, 3, 5]

final List และ const List

เราสามารถสร้างลิสต์ที่ไม่สามารถเปลี่ยนแปลงค่าได้ด้วยการใช้ final และ const ซึ่งมีข้อแตกต่างกันคือ

var list = const [1, 2, 3];

// const List ไม่สามารถแก้ไขหรือเพิ่ม/ลดข้อมูลได้
list.add(4);        // Error!

// แต่เราสามมรถกำหนดค่าให้ตัวแปร list ใหม่ได้
list = [5, 6, 7];

ส่วน final นั้นจะเป็นแบบนี้

final list = [1, 2, 3];

// final ถูกเซ็ตที่ตัวแปร List ไม่ใช่ค่าของลิสต์
list.add(4);        // Ok!

// แต่เราไม่สามมรถกำหนดค่าใหม่ให้ตัวแปร list ใหม่ได้
list = [5, 6, 7];   // Error!

และเราสามารถสร้างลิสต์ที่เป็นทั้ง final และ const ได้

final list = const [1, 2, 3];

Spread Operator

เราสามารถใช้ ... เพื่อแยกลิสต์ได้ หากใช้เขียน JavaScript มาก่อนน่าจะคุ้นกับคำสั่งนี้

var list1 = [1, 2, 3, 4];
var list2 = [0, ...list1, 5];
//list2 is [0, 1, 2, 3, 4, 5]

และเพราะตัวแปรใน Dart สามารถเป็น null ได้ เราสามารถใช้คำสั่ง null-aware spread operator ได้

var list1;
var list2 = [0, ...?list1, 5];
//list2 is [0, 5]

นอกจากนี้เรายังสามารถใช้คำสั่ง if และ for ในลิสต์ได้ด้วย

var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];

var listOfInts = [1, 2, 3];
var listOfStrings = [
  'number 0',
  for (var i in listOfInts) 'number $i'
];

Map

map เป็น Data Structure อีกตัวหนึ่ง ซึ่งเป็นแบบ key-value (คล้ายๆ List ที่สามารถกำหนดชื่อของ index ได้)

Map data = {'id': 1, 'name': 'Ann'};
print(data['id']);      // 1
print(data['name']);    // Ann

และเหมือนกับ List คือถ้าเราไม่ได้กำหนดชนิดข้อมูลมันจะถูกกำหนดอัตโนมัติให้เป็น Map

ดังนั้นคำแนะนำในการใช้ Map คือควรกำหนดชนิดของ key และ value ทุกครั้ง

Map<String, int> score = {'A': 100, 'B': 200};
//หรือ
var score = <String, int>{'A': 100, 'B': 200};

การเพิ่มหรือลบ item ออกจาก Map

การเพิ่ม item ลงใน Map นั้นทำง่ายกว่า List เพราะเราสามารถกำหนดค่าลงไปตรงๆ ได้เลย

Map<String, int> data = {'A': 100};
data['B'] = 200;

ส่วนการลบข้อมูลออกเราจะใช้คำสั่ง remove ซึ่งจะใช้การกำหนดด้วย key ไม่ใช่ value

Map<String, int> data = {'A': 1, 'B': 2};
data.remove('A');
print(data);    // {B: 2}

หรือถ้าต้องการตั้งเงื่อนไขการลบเองก็สามารถใช้คำสั่ง removeWhere

Map<String, int> data = {
    'A': 1, 
    'B': 2, 
    'C': 3,
};
data.removeWhere((key, value){
    return value==2 || key=='A';
});
print(data);    // {C: 3}

final Map และ const Map

เหมือนกับ List นั่นคือเราสามารถเราสามารถกำหนด const Map ซึ่งจะไม่สามารถเปลี่ยนแปลงค่าอะไรได้เลย

final constantMap = const {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

.map() method

เมธอด map เป็นคนละตัวกันคลาส Map นะ เป็นเมธอดที่ใช้ได้กับทั้ง List และ Map เลย

วิธีการใช้ map คือหากเราต้องการจะแปลงค่าทุกค่าใน List หรือ Map ให้เป็นอีกค่าหนึ่ง เช่นถ้าเรามี items ตัวหนึ่งที่ต้องการเอาค่าทุกค่าไป x 10

จากปกติที่เราเขียนกันแบบใช้ลูปธรรมดา

List<int> items = [1, 2, 3, 4];
List<int> items10;
for(var item in items){
    items10.add(item * 10);
}

// items10 is [10, 20, 30, 40]

ก็สามารถเขียนเป็นแบบนี้ได้

List<int> items = [1, 2, 3, 4];
List<int> items10 = items.map((item) => item * 10).toList();

// items10 is [10, 20, 30, 40]

เวลาเขียนให้สร้างฟังก์ชันสำหรับบอกว่า ถ้าเรามี item หนึ่ง จะแปลงให้มันเป็นค่าอะไรเท่านั้นพอ

แต่ map ของ List นั้นไม่ได้รีเทิร์นค่ากลับมาเป็น List แต่ให้ค่ากลับมาเป็น Iterable นั่นคือถ้าเราต้องการให้มันกลับมาเป็น List จะต้องใช้คำสั่ง toList() ต่ออีกที

ส่วนถ้าเป็น Map อาจจะใช้ยากขึ้นหน่อยเพราะจะต้องเขียนทั้ง key และ value และต้องตอบค่ากลับมาเป็น MapEntry แทน

Map<String, int> data = {
    'A': 1, 
    'B': 2, 
    'C': 3,
};

Map<String, int> data2 = data.map((key, value){
    return MapEntry('#$key', value * 10);
});

// data2 is {#A: 10, #B: 20, #C: 30}

Convert List-Map

เราสามารถแปลง List --> Map ได้โดย

List<String> data = ['A', 'B', 'C'];

Map<int, String> m = data.asMap();
// m is {0: A, 1: B, 3: C}

ซึ่ง key ของ Map ที่ได้ออกมาจะเป็น int และได้ค่ามาจาก index ของ List นั้นๆ

ส่วนการแปลง Map --> List นั้นง่ายกว่าแต่เราจะต้องเลือกว่าจะเอา List of key หรือ List of value

Map<String, int> m = {'A': 10, 'B': 20, 'C': 30};

m.keys      // [A, B, C]
m.values    // [10, 20, 30]
18 Total Views 3 Views Today
Ta

Ta

สิ่งมีชีวิตตัวอ้วนๆ กลมๆ เคลื่อนที่ไปไหนโดยการกลิ้ง .. ถนัดการดำรงชีวิตโดยไม่โดนแสงแดด
ปัจจุบันเป็น Senior Software Engineer อยู่ที่ Centrillion Technology
งานอดิเรกคือ เขียนโปรแกรม อ่านหนังสือ เขียนบทความ วาดรูป และ เล่นแบดมินตัน

You may also like...