Sắp xếp danh sách trong tiếng Việt bằng PHP

Thú thực với các bác là cả đời mình, em chưa bao giờ nghiên cứu một thuật toán nào về sắp xếp cả (vì một thằng khối C như em thì chỉ nghe đến “thuật toán” là đã hãi rồi). Bình thường thì, khi có nhu cầu sắp xếp, em dùng sort. Cứ echo input | sort > output, thế là xong! Nhưng giờ thì phải dùng PHP. Thực ra thì cũng có thể dùng tham số SORT_LOCALE_STRING, nhưng có phải máy chủ nào cũng có phần locale tiếng Việt ngon lành đâu.

Giá kể nó cứ hỗ trợ ngon lành như Java thì khoẻ re. Không biết PHP6 nó thế nào nhỉ? Thôi, lại phải ngồi lưng trâu vậy.

<?php
function ViPAencode($text) {
	$origin = array(
	'/([0-9])/',
	'/a/', '/à/', '/ả/', '/ã/', '/á/', '/ạ/',
	'/ă/', '/ằ/', '/ẳ/', '/ẵ/', '/ắ/', '/ặ/',
	'/â/', '/ầ/', '/ẩ/', '/ẫ/', '/ấ/', '/ậ/',
	'/e/', '/è/', '/ẻ/', '/ẽ/', '/é/', '/ẹ/',
	'/ê/', '/ề/', '/ể/', '/ễ/', '/ế/', '/ệ/',
	'/i/', '/ì/', '/ỉ/', '/ĩ/', '/í/', '/ị/',
	'/o/', '/ò/', '/ỏ/', '/õ/', '/ó/', '/ọ/',
	'/ô/', '/ồ/', '/ổ/', '/ỗ/', '/ố/', '/ộ/',
	'/ơ/', '/ờ/', '/ở/', '/ỡ/', '/ớ/', '/ợ/',
	'/u/', '/ù/', '/ủ/', '/ũ/', '/ú/', '/ụ/',
	'/ư/', '/ừ/', '/ử/', '/ữ/', '/ứ/', '/ự/',
	'/y/', '/ỳ/', '/ỷ/', '/ỹ/', '/ý/', '/ỵ/',
	'/A/', '/À/', '/Ả/', '/Ã/', '/Á/', '/Ạ/',
	'/Ă/', '/Ằ/', '/Ẳ/', '/Ẵ/', '/Ắ/', '/Ặ/',
	'/Â/', '/Ầ/', '/Ẩ/', '/Ẫ/', '/Ấ/', '/Ậ/',
	'/E/', '/È/', '/Ẻ/', '/Ẽ/', '/É/', '/Ẹ/',
	'/Ê/', '/Ề/', '/Ể/', '/Ễ/', '/Ế/', '/Ệ/',
	'/I/', '/Ì/', '/Ỉ/', '/Ĩ/', '/Í/', '/Ị/',
	'/O/', '/Ò/', '/Ỏ/', '/Õ/', '/Ó/', '/Ọ/',
	'/Ô/', '/Ồ/', '/Ổ/', '/Ỗ/', '/Ố/', '/Ộ/',
	'/Ơ/', '/Ờ/', '/Ở/', '/Ỡ/', '/Ớ/', '/Ợ/',
	'/U/', '/Ù/', '/Ủ/', '/Ũ/', '/Ú/', '/Ụ/',
	'/Ư/', '/Ừ/', '/Ử/', '/Ữ/', '/Ứ/', '/Ự/',
	'/Y/', '/Ỳ/', '/Ỷ/', '/Ỹ/', '/Ý/', '/Ỵ/',
	'/d/', '/đ/', '/D/', '/Đ/');

	$codes = array(
	"-$1",
	'a0', 'a02', 'a03', 'a04', 'a05', 'a06',
	'a1', 'a12', 'a13', 'a14', 'a15', 'a16',
	'a2', 'a22', 'a23', 'a24', 'a25', 'a26',
	'e0', 'e02', 'e03', 'e04', 'e05', 'e06',
	'e1', 'e12', 'e13', 'e14', 'e15', 'e16',
	'i0', 'i02', 'i03', 'i04', 'i05', 'i06',
	'o0', 'o02', 'o03', 'o04', 'o05', 'o06',
	'o1', 'o12', 'o13', 'o14', 'o15', 'o16',
	'o2', 'o22', 'o23', 'o24', 'o25', 'o26',
	'u0', 'u02', 'u03', 'u04', 'u05', 'u06',
	'u1', 'u12', 'u13', 'u14', 'u15', 'u16',
	'y0', 'y02', 'y03', 'y04', 'y05', 'y06',
	'A0', 'A02', 'A03', 'A04', 'A05', 'A06',
	'A1', 'A12', 'A13', 'A14', 'A15', 'A16',
	'A2', 'A22', 'A23', 'A24', 'A25', 'A26',
	'E0', 'E02', 'E03', 'E04', 'E05', 'E06',
	'E1', 'E12', 'E13', 'E14', 'E15', 'E16',
	'I0', 'I02', 'I03', 'I04', 'I05', 'I06',
	'O0', 'O02', 'O03', 'O04', 'O05', 'O06',
	'O1', 'O12', 'O13', 'O14', 'O15', 'O16',
	'O2', 'O22', 'O23', 'O24', 'O25', 'O26',
	'U0', 'U02', 'U03', 'U04', 'U05', 'U06',
	'U1', 'U12', 'U13', 'U14', 'U15', 'U16',
	'Y0', 'Y02', 'Y03', 'Y04', 'Y05', 'Y06',
	'd81', 'd91', 'D81', 'D91');

	$encoded = preg_replace($origin, $codes, $text);
	return $encoded;
}

function ViSort($list) { // $list must be an array
	for ($i = 0; $i < count($list); $i++) {
		$list_encoded[$i] = ViPAencode($list[$i]);
		$list_encoded[$i] = preg_replace("/\b(\w+)([012])([2-6])(\w+)?\b/", "$1$2$4$3", $list_encoded[$i]);
	}
	// Convert to lowercase
	$list_encoded_lowercase = array_map('strtolower', $list_encoded);
	// Sort list by encoded list (in lowercase),
	// so we don't have to write the crazy function called 'ViPAdecode' :p 
	array_multisort($list_encoded_lowercase, SORT_ASC, SORT_STRING, $list);

	return $list;
}

function ViSortByName($list) { // $list must be an array
// TODO: The priority must be checked again!
// There are some related articles on Journal of Language
// (printed about 5 years ago)...

	for ($i = 0; $i < count($list); $i++) {
		$list_reversed = explode(" ", trim($list[$i]));
		$list_reversed = array_reverse($list_reversed);
		$list[$i] = implode(" ", $list_reversed);
	}

	$list_sorted = ViSort($list);

	for ($j = 0; $j < count($list_sorted); $j++) {
		$list_unreversed = explode(" ", $list_sorted[$j]);
		$list_unreversed = array_reverse($list_unreversed);
		$list_sorted[$j] = implode(" ", $list_unreversed);
	}

	return $list_sorted;
}
?>

Chạy được, nhưng cực chậm. Nếu phải sắp xếp danh sách gồm 6651 âm tiết tiếng Việt (mỗi âm nằm trên một dòng) thì nó mất những hơn 2,5 giây, trong khi, nếu dùng sort(), chỉ mất khoảng 0,1 giây 😥 . Thôi thì, khả năng mới chỉ có vậy, đành tạm thế vậy…

Một bình luận

  1. Mình có thể work around = hàm này (chỉ áp dụng cho TV).
    function e_sortcb($a, $b) {
    $map = array(
    ‘Ă’ => ‘Az’,
    ‘Ằ’ => ‘Azz’,
    ‘Ắ’ => ‘Azzz’,
    ‘Ẳ’ => ‘Azzzz’,
    ‘Ẵ’ => ‘Azzzzz’,
    ‘Ặ’ => ‘Azzzzzz’,
    ‘Â’ => ‘Azzzzzzz’,
    ‘Ầ’ => ‘Azzzzzzz’,
    ‘Ấ’ => ‘Azzzzzzzz’,
    ‘Ẩ’ => ‘Azzzzzzzzz’,
    ‘Ẫ’ => ‘Azzzzzzzzzz’,
    ‘Ậ’ => ‘Azzzzzzzzzzz’,
    ‘ă’ => ‘az’,
    ‘ằ’ => ‘azz’,
    ‘ắ’ => ‘azzz’,
    ‘ẳ’ => ‘azzzz’,
    ‘ẵ’ => ‘azzzzz’,
    ‘ặ’ => ‘azzzzzz’,
    ‘â’ => ‘azzzzzzz’,
    ‘ầ’ => ‘azzzzzzzz’,
    ‘ấ’ => ‘azzzzzzzzz’,
    ‘ẩ’ => ‘azzzzzzzzzz’,
    ‘ẫ’ => ‘azzzzzzzzzzz’,
    ‘ậ’ => ‘azzzzzzzzzzzz’,
    ‘Đ’ => ‘Dz’,
    ‘đ’ => ‘dz’,
    ‘Ê’ => ‘Ez’,
    ‘Ề’ => ‘Ezz’,
    ‘Ế’ => ‘Ezzz’,
    ‘Ể’ => ‘Ezzzz’,
    ‘Ễ’ => ‘Ezzzzz’,
    ‘Ệ’ => ‘Ezzzzzz’,
    ‘ê’ => ‘ezzzzzzz’,
    ‘ề’ => ‘ezzzzzzzz’,
    ‘ê’ => ‘ezzzzzzzzz’,
    ‘ể’ => ‘ezzzzzzzzzz’,
    ‘ễ’ => ‘ezzzzzzzzzzz’,
    ‘ệ’ => ‘ezzzzzzzzzzzz’,
    ‘Ô’ => ‘Oz’,
    ‘Ồ’ => ‘Ozz’,
    ‘Ố’ => ‘Ozzz’,
    ‘Ổ’ => ‘Ozzzz’,
    ‘Ỗ’ => ‘Ozzzzz’,
    ‘Ộ’ => ‘Ozzzzzz’,
    ‘Ơ’ => ‘Ozzzzzzz’,
    ‘Ờ’ => ‘Ozzzzzzzz’,
    ‘Ớ’ => ‘Ozzzzzzzzz’,
    ‘Ở’ => ‘Ozzzzzzzzz’,
    ‘Ỡ’ => ‘Ozzzzzzzzzz’,
    ‘Ợ’ => ‘Ozzzzzzzzzzz’,
    ‘ô’ => ‘oz’,
    ‘ồ’ => ‘ozz’,
    ‘ố’ => ‘ozzz’,
    ‘ổ’ => ‘ozzzz’,
    ‘ỗ’ => ‘ozzzzz’,
    ‘ộ’ => ‘ozzzzzz’,
    ‘ơ’ => ‘ozzzzzzz’,
    ‘ờ’ => ‘ozzzzzzzz’,
    ‘ớ’ => ‘ozzzzzzzzz’,
    ‘ở’ => ‘ozzzzzzzzzz’,
    ‘ỡ’ => ‘ozzzzzzzzzzz’,
    ‘ợ’ => ‘ozzzzzzzzzzzz’,
    ‘Ư’ => ‘Uz’,
    ‘Ừ’ => ‘Uzz’,
    ‘Ứ’ => ‘Uzzz’,
    ‘Ử’ => ‘Uzzzz’,
    ‘Ữ’ => ‘Uzzzzz’,
    ‘Ự’ => ‘Uzzzzzz’,
    ‘ư’ => ‘uz’,
    ‘ừ’ => ‘uzz’,
    ‘ứ’ => ‘uzzz’,
    ‘ử’ => ‘uzzzz’,
    ‘ữ’ => ‘uzzzzz’,
    ‘ự’ => ‘uzzzzzz’,
    );
    $keys = array_keys($map);
    $vals = array_values($map);
    $a = str_replace($keys, $vals, $a);
    $b = str_replace($keys, $vals, $b);
    if ($a == $b) {
    return 0;
    }
    return ($a < $b) ? -1 : 1;
    }

    Sau đó dùng usort với hàm này.

Bình luận

Website này sử dụng Akismet để hạn chế spam. Tìm hiểu bình luận của bạn được duyệt như thế nào.