[Collection] Practice 5: Building a Lookup Table
Tôi có 1 danh sách nhân viên và bạn cần chuyển đổi nó vào bảng tra cứu mapping địa chỉ email với tên của họ.
Mảng đầu vào1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$employees = [
[
'name' => 'John',
'department' => 'Sales',
'email' => 'john@example.com'
],
[
'name' => 'Jane',
'department' => 'Marketing',
'email' => 'jane@example.com'
],
[
'name' => 'Dave',
'department' => 'Marketing',
'email' => 'dave@example.com'
],
];
Và chúng tôi cần tạo 1 mảng tra cứu có dạng như sau:1
2
3
4
5$emailLookup = [
'john@example.com' => 'John',
'jane@example.com' => 'Jane',
'dave@example.com' => 'Dave',
];
Vậy chúng ta có thể sử dụng collections và higher order functions?
Map to nowhere
Khi mà chuyển đổi dữ liệu ta nghĩ ngay đến map
1
2
3$emailLookup = $employees->map(function ($employee) {
return $employee['name'];
});
Điều này sẽ cho bạn 1 danh sách tên những nhân viên, mà khoan, key đâu??
PHP’s Array Identity Crisis: Cuộc khủng hoảng bản sắc
Vấn đề tùy chỉnh keys trong khi sử dụng toán tử map
là điều mà tôi nhận được khá thường xuyên.
Tôi nghĩ lý do mà làm cho vấn đế này trở nên hóc búa là bởi vì PHP, chúng ta sử dụng kiểu dữ liệu giống nhau đại diện cho cả list và dictionary.
Quên PHP trong 1 vài phút và giả như chúng ta đang cố giải quyết vấn đề với Javascript
… (xem ví dụ về Object)
Vì vậy chúng ta không thực sự chuyển đổi 1 mảng thành mảng khác, chúng ta đang giảm (reducing) mảng ban đầu thành 1 đối tượng đơn.
Thực thi chuyển đổi trong Javascript với reduce sẽ có dạng như sau:1
2
3
4const emailLookup = employees.reduce(function (emailLookup, employee) {
emailLookup[employee.email] = employee.name;
return emailLookup;
}, {});
Và Laravel cũng tương tự:1
2
3
4
5$emailLookup = $employees->reduce(function ($emailLookup, $employee) {
$emailLookup[$employee['email']] = $employee['name'];
return $emailLookup;
}, []);
A Reusable Abstraction: Một trừu tượng hóa có thể sử dụng lại
Mặc dù chúng ta đã giải quyết được vấn đề ban đầu nhưng tôi thực sự chưa hài lòng với việc đọc code như thế nào?
Như tôi đã nhắc ở phần trước cuốn sách, tôi thường xuyên nhìn thấy reduce
như 1 dấu hiệu rằng tôi đang quên mất cách diễn đạt dễ hình dung hơn được xây dựng với reduce
?
=> Tức là xây dựng function cao hơn, giàu tính biểu đạt hơn dựa vào reduce
.
Thật tuyệt vời nếu chúng ta tạo 1 toán tử với tên toAssoc()
sử dụng để chuyển 1 danh sách vào mảng kết hợp (associative array), nhưng làm sao chúng ta có thể xác định được cả key và value?
Learning from Other Languages
Ruby có kiểu mảng kết hợp là Hash.
Bạn có thể chuyển 1 Enumerable vào trong Hash bằng cách sử dụng phương thức to_h
miễn là enumerable tạo thành cặp [key, value]
1
2
3
4
5employees = [
['john@example.com', 'John'],
['jane@example.com', 'Jane'],
['dave@example.com', 'Dave'],
]
và gọi phương thức to_h1
2
3
4
5
6employees.to_h
# => {
# 'john@example.com' => 'John',
# 'jane@example.com' => 'Jane',
# 'dave@example.com' => 'Dave',
# }
The toAssoc
Macro
1 | Collection::macro('toAssoc', function () { |
Và có thể sử dụng1
2
3
4
5
6
7
8
9
10$emailLookup = collect([
['john@example.com', 'John'],
['jane@example.com', 'Jane'],
['dave@example.com', 'Dave'],
])->toAssoc();
// => [
// 'john@example.com' => 'John',
// 'jane@example.com' => 'Jane',
// 'dave@example.com' => 'Dave',
// ]
Mapping to Pairs
Tất nhiên là sử dụng map, và khi đặt mọi thứ cùng nhau, nó sẽ như sau :D1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$emailLookup = collect([
[
'name' => 'John',
'department' => 'Sales',
'email' => 'john@example.com'
],
[
'name' => 'Jane',
'department' => 'Marketing',
'email' => 'jane@example.com'
],
[
'name' => 'Dave',
'department' => 'Marketing',
'email' => 'dave@example.com'
],
])->map(function ($employee) {
return [$employee['email'], $employee['name']];
})->toAssoc();
Nếu bạn muốn cải tiến thêm 1 bước nữa, bạn hoàn toàn có thể xây dựng một phương thức với tên mapToAssoc
giúp chúng ta chuyển đổi dữ liệu trong 1 bước:1
2
3Collection::macro('mapToAssoc', function ($callback) {
return $this->map($callback)->toAssoc();
});
Và chúng ta có thể sử dụng nó như sau:1
2
3$emailLookup = $employees->mapToAssoc(function ($employee) {
return [$employee['email'], $employee['name']];
});
Pretty slick!
[Collection] Practice 5: Building a Lookup Table
http://yoursite.com/2019/12/28/Collection-Practice-5-Building-a-Lookup-Table/