Cách đảo ngược chuỗi string trong Javascript

Đảo ngược chuỗi là một câu hỏi khá phổ biến khi các bạn lập trình hay đi phỏng vấn (mình đoán thế, vì mình chưa gặp :P). Nếu được hỏi, mình đoán câu hỏi sẽ như thế này: Làm thế nào để đảo ngược một chuỗi mà không dùng hàm có sẵn trong JS vậy em? Hoặc áp dụng đệ quy, em đảo chuỗi cho anh xem nào :v.

Thực ra trên mạng có rất nhiều cách, và cũng có rất nhiều người viết về chủ đề này rồi. Hôm này mình chỉ tổng hợp lại cách mình thích dùng nhất.

Cơ bản nhất, sử dụng vòng lặp (loop)

Có lẽ cơ bản nhất, cách bung ra trong đầu bạn đầu tiên khi gặp yêu cầu này là sử dụng for loop. Cách tiếp cận sử dụng for loop về cơ bản là lặp từng ký tự trong chuỗi đó từ đuôi tới đầu (bạn có thể cho lặp từ đầu tới đuôi) và tạo một chuỗi mới để “chứa” kết quả.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function reverseString(string) {
let reversedString = '';

// Chúng ta sẽ lặp từ đuôi tới đầu -> để đảo ngược
// length sử dụng được với array và string, nên ta có thể lợi dụng điều này:v
for (let i = string.length - 1; i >= 0; i--) {
// lấy giá trị tại index `i` cũng y như array vậy
reversedString += string[i]; // hoặc reversedString = reversedString + string[i];
}

return reversedString;
}

reverseString('hello'); // "olleh"
reverseString('My name is Thang'); // "gnahT si eman yM"

Bạn có thể sử dụng cái khác thay cho for, như while hoặc trong ES6 thì là for … of, forEach chẳng hạn.

Sử dụng hàm reduce()

Đây có lẽ là cách giải quyết khá độc, khi mà cũng không nhiều người để ý đến hàm ni :D. Hàm reduce chỉ dùng với array nên chúng ta cần làm là chuyển chuỗi về array sau đó dùng reduce để đảo ngược lại.

Hàm reduce nhận vào 2 tham số (tham số thứ hai là tham số tùy chọn, có hay không cũng được). Tham số đầu tiên nhận vào là một function (callback) giang hồ gọi nó là reducer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function reverseString(string) {
// chuyển string về mảng
const splitString = string.split('');

// điểm cốt yếu trong cách này là callback `reducer`
// tham số 1 là một callback
// tham số hai là giá trị ban đầu cho tham số accumulatorString của callback
const reversedString = splitString.reduce(function (accumulatorString, currentChar) {
return currentChar + accumulatorString;
}, '');
/* cách hoạt động như sau
số lần chạy của reducer sẽ bằng với ['h', 'e', 'l', 'l', 'o'].length -> chạy 5 lần

function reducer (accumulatorString, currentChar) {
// giá trị ban đầu của accumulatorString = ''
// currentChar sẽ nhận giá trị từng phần tử trong mảng splitString

// chạy lần 1
accumulatorString = 'h' + '';
// -> 'h'

// chạy lần 2
accumulatorString = 'e' + 'h'
// -> 'eh'

// chạy lần 3
accumulatorString = 'l' + 'eh';
// -> 'leh'

// chạy lần 4
accumulatorString = 'l' + 'leh'
// -> 'lleh'

// chạy lần 4
accumulatorString = 'o' + 'lleh'
// -> 'olleh'

// sau khi chạy xong reducer sẽ trả về `accumulatorString`

return accumulatorString; // 'olleh'
};
*/

return reversedString;
}
reverseString('hello');

Sử dụng đệ quy

Để sử dụng đệ quy chúng ta cần dùng đến hàm substring()charAt().

Hàm substring() sẽ trả về một phần trong chuỗi đoạn ở giữa vị trí bắt đầu và vị trí kết thúc.
Syntax của substring: str.substring(indexStart[, indexEnd]). indexEnd không bắt buộc, vì khi không truyền vào, thì nó hiểu bạn sẽ lấy tất cả phần còn lại.

1
'hello'.substring(1); // 'ello'

Hàm charAt sẽ trả về ký tự tại vị trí chỉ định trong chuỗi.

1
'hello'.charAt(1); // 'e'

Ví dụ cách đảo chuỗi bằng đệ quy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function reverseString(string) {
// Đây là điều kiện để 'giải thoát' khỏi đệ quy
if (string === '') {
return '';
} else {
return reverseString(string.substring(1)) + string.charAt(0);
}
/*
Mô phỏng cách hoạt động
Đây là cách gọi lồng nhau của đệ quy, bạn không cần gọi nhiều lần như thế này đâu nhé
Gọi hàm check: string === "?" // string khác rỗng -> reverseString(string.subst(1)) + string.charAt(0)
1 – reverseString('Hello') trả về reverseString('ello') + 'h'
2 – reverseString('ello') trả về reverseString('llo') + 'e'
3 – reverseString('llo') trả về reverseString('lo') + 'l'
4 – reverseString('lo') trả về reverseString('o') + 'l'
5 – reverseString('o') trả về reverseString('') + 'o'
Sau khi được "trả về" thì nó gọi hàm trả về :v
5 gọi hàm được trả về reverseString('') + 'o' = 'o'
4 gọi hàm được trả về reverseString('o') + 'l' = 'o' + 'l'
3 gọi hàm được trả về reverseString('lo') + 'l' = 'o' + 'l' + 'l'
2 gọi hàm được trả về reverserString('llo') + 'e' = 'o' + 'l' + 'l' + 'e'
1 gọi hàm được trả về reverserString('ello') + 'h' = 'o' + 'l' + 'l' + 'e' + 'h'
*/
}
reverseString('hello'); // 'olleh'

Viết ngắn gọn bằng toán tử ba ngôi:

1
2
3
4
function reverseString(string) {
return (string === '') ? '' : reverseString(string.substr(1)) + string.charAt(0);
}
reverseString('hello');

Lưu ý: có thể thay charAt(0) bằng cách gọi trực tiếp như string[0].

Như các bạn đã biết, về bản chất số lần đệ quy sẽ bằng với độ dài của chuỗi, lại thêm mỗi lần đệ quy nó sẽ lưu vào stack để gọi lại. Chính vì rứa, sử dụng đệ quy với chuỗi dài sẽ rất chậm.

Sử dụng hàm có sẵn

Hàm có sẵn ở đây là reverse(). Tuy nhiên hàm này chỉ dành cho array mà thôi, chính vì thế chúng ta cần chuyển chuỗi về array bằng cách sử dụng hàm split(). Sau khi reverse xong thì ta lại dùng hàm join để nối các phần tử trong mảng thành chuỗi trở lại.

Ví dụ (cách viết dài dòng)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function reverseString(string) {
// Sử dụng method split để chuyển chuỗi thành array
const splitString = string.split('');
// ví dụ: const splitString = 'hello'.split('');
// -> ['h', 'e', 'l', 'l', 'o']

// Dùng method có sẵn để đảo ngược mảng
const reversedArray = splitString.reverse();
// ví dụ const reverseArray = ['h', 'e', 'l', 'l', 'o'].reverse();
// ['o', 'l', 'l', 'e', 'h']

// Dùng hàm join để nối các phần tử lại
const joinArray = reversedArray.join('');
// ví dụ const joinArray = ['o', 'l', 'l', 'e', 'h'].join('');
// 'olleh'

return joinArray; // 'olleh'
}

reverseString('hello');

Cách viết nhanh gọn:

1
2
3
4
function reverseString(string) {
return string.split('').reverse().join('');
}
reverseString('hello');

Sử dụng toán tử spread trong ES6

ES6 giới thiệu một cách để convert chuỗi sang array nhanh chóng đó là toán tử spread. Về cơ bản giống với cách sử dụng hàm có sẵn, nhưng trông có vẻ hù người tốt hơn.

1
2
3
4
function reverseString(string) {
return [...string].reverse().join('');
}
reverseString('hello');

Cuối cùng

Tuy không có chi to tát, cơ mà biết đâu khi đi phỏng vấn họ lại hỏi thì sao. :)) Mình hi vọng nó sẽ giúp ích cho các bạn.

Tham khảo từ:

 Comments
Comment plugin failed to load
Loading comment plugin