DEV Community

Ta for tamemo

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

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

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

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

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

แต่ก็ตามสไตล์ภาษามี 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
Enter fullscreen mode Exit fullscreen mode

ในภาษา 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'];
//                               ^
Enter fullscreen mode Exit fullscreen mode

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

loop

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

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

หรือใช้ for-in

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

การเพิ่มหรือลบ 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]
Enter fullscreen mode Exit fullscreen mode

ในภาษา 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]
Enter fullscreen mode Exit fullscreen mode

ส่วนการลบ 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]
Enter fullscreen mode Exit fullscreen mode

final List และ const List

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

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

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

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

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

final list = [1, 2, 3];

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

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

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

final list = const [1, 2, 3];
Enter fullscreen mode Exit fullscreen mode

Spread Operator

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

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

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

var list1;
var list2 = [0, ...?list1, 5];
//list2 is [0, 5]
Enter fullscreen mode Exit fullscreen mode

นอกจากนี้เรายังสามารถใช้คำสั่ง 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'
];
Enter fullscreen mode Exit fullscreen mode

Map

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

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

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

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

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

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

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

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

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

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

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

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

final Map และ const Map

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

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

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

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

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

// items10 is [10, 20, 30, 40]
Enter fullscreen mode Exit fullscreen mode

เวลาเขียนให้สร้างฟังก์ชันสำหรับบอกว่า ถ้าเรามี 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}
Enter fullscreen mode Exit fullscreen mode

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

ซึ่ง 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]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)