Operasi CRUD sudah berfungsi, tetapi pengguna program sering perlu mencari produk berdasarkan nama yang tidak persis sama. Pengguna toko bisa mengetik “ber” untuk menemukan “Beras” dan “Beras Merah”. Pada bagian ini, kita tambahkan pencarian nama dengan substring case-insensitive dan dua algoritma pengurutan: by-name dan by-price.
Konsep #
Pencarian substring case-insensitive memerlukan dua langkah:
- Bandingkan setiap karakter pasangan string dengan
tolower()agar abaikan besar/kecil huruf. - Geser jendela pencarian satu karakter pada satu waktu di string utama (haystack).
Pengurutan kita implementasikan dengan bubble sort. Algoritma ini sederhana dan mudah dipahami, dengan kompleksitas O(n^2). Untuk 256 produk maksimum, performa bubble sort masih sangat memadai (sekitar 65 ribu operasi paling buruk).
Implementasi #
Fungsi helper untuk case-insensitive substring match:
/* Helper: case-insensitive substring match */
static int contains_ci(const char *haystack, const char *needle) {
if (needle == NULL || needle[0] == '\0') {
return 1; /* keyword kosong = match semua */
}
if (haystack == NULL) {
return 0;
}
for (const char *h = haystack; *h != '\0'; h++) {
const char *hi = h;
const char *ni = needle;
while (*hi != '\0' && *ni != '\0' &&
tolower((unsigned char)*hi) == tolower((unsigned char)*ni)) {
hi++;
ni++;
}
if (*ni == '\0') {
return 1;
}
}
return 0;
}Fungsi inventory_find_by_name:
int inventory_find_by_name(Inventory *inv, const char *keyword,
Product *out, int out_cap) {
if (inv == NULL || out == NULL || out_cap <= 0) {
return 0;
}
int found = 0;
for (int i = 0; i < inv->count && found < out_cap; i++) {
if (contains_ci(inv->items[i].name, keyword)) {
out[found++] = inv->items[i];
}
}
return found;
}Fungsi sort dengan bubble sort dan swap helper:
static void swap_product(Product *a, Product *b) {
Product tmp = *a;
*a = *b;
*b = tmp;
}
void inventory_sort_by_name(Inventory *inv) {
if (inv == NULL || inv->count < 2) {
return;
}
for (int i = 0; i < inv->count - 1; i++) {
for (int j = 0; j < inv->count - i - 1; j++) {
if (strcmp(inv->items[j].name,
inv->items[j + 1].name) > 0) {
swap_product(&inv->items[j], &inv->items[j + 1]);
}
}
}
}
void inventory_sort_by_price(Inventory *inv) {
if (inv == NULL || inv->count < 2) {
return;
}
for (int i = 0; i < inv->count - 1; i++) {
for (int j = 0; j < inv->count - i - 1; j++) {
if (inv->items[j].price > inv->items[j + 1].price) {
swap_product(&inv->items[j], &inv->items[j + 1]);
}
}
}
}Verifikasi pencarian dan pengurutan:
/* demo_search.c - Uji coba pencarian dan sort */
#include <stdio.h>
#include "inventory.h"
int main(void) {
Inventory inv;
inventory_init(&inv);
inventory_add(&inv, "Chiki", 5000.0, 10);
inventory_add(&inv, "Apel", 8000.0, 5);
inventory_add(&inv, "Bola", 3000.0, 7);
inventory_add(&inv, "Beras Merah", 75000.0, 3);
printf("Cari 'ber' (case-insensitive):\n");
Product out[16];
int n = inventory_find_by_name(&inv, "ber", out, 16);
printf("Ditemukan %d produk:\n", n);
for (int i = 0; i < n; i++) {
product_print(&out[i]);
}
printf("\nSetelah sort by name:\n");
inventory_sort_by_name(&inv);
for (int i = 0; i < inv.count; i++) {
product_print(&inv.items[i]);
}
printf("\nSetelah sort by price:\n");
inventory_sort_by_price(&inv);
for (int i = 0; i < inv.count; i++) {
product_print(&inv.items[i]);
}
return 0;
}Output yang Dihasilkan:
Cari 'ber' (case-insensitive):
Ditemukan 1 produk:
[000004] Beras Merah Rp 75000.00 stok= 3
Setelah sort by name:
[000002] Apel Rp 8000.00 stok= 5
[000004] Beras Merah Rp 75000.00 stok= 3
[000001] Chiki Rp 5000.00 stok= 10
[000003] Bola Rp 3000.00 stok= 7
Setelah sort by price:
[000003] Bola Rp 3000.00 stok= 7
[000001] Chiki Rp 5000.00 stok= 10
[000002] Apel Rp 8000.00 stok= 5
[000004] Beras Merah Rp 75000.00 stok= 3Mari kita telaah baris-baris kode di atas:
- Baris 4-7 (
contains_ciearly return): Bilaneedlekosong, kembalikan 1 (match semua). Ini memudahkan UI: pengguna yang menekan Enter tanpa mengetik apa pun akan mendapatkan seluruh daftar. - Baris 11-19 (inner loop): Untuk setiap posisi awal di
haystack, kita coba matchneedlekarakter per karakter. Loop berhenti saat salah satu string habis atau ketemu mismatch. - Baris 21-23 (
*ni == '\0'): Bilaneedlehabis (semua karakter cocok), maka substring ditemukan. - Baris 39-44 (
inventory_find_by_name): Salin produk yang match ke bufferouthinggaout_cap. Batas ini melindungi pemanggil dari buffer overflow bila hasil pencarian sangat banyak. - Baris 56-65 (
inventory_sort_by_name): Bubble sort membandingkan pasangan elemen berdekatan dan menukar bila urutan salah. Loop luar memastikan setiap iterasi menempatkan satu elemen terbesar di akhir, sehingga loop dalam bisa berhenti lebih awal. - Baris 25-28 (
swap_product): Tukar dua struct utuh dengan assignment. C11 mengizinkan assignment struct secara langsung, lebih cepat daripadamemcpymanual.
Kode Lengkap #
Kode lengkap untuk fungsi pencarian dan sort ada di inventory.c. Fungsi swap_product dan contains_ci dibuat static karena hanya dipakai internal.
Pengujian #
Selain demo di atas, kita perlu memastikan edge case:
- Pencarian dengan keyword kosong mengembalikan seluruh produk.
- Pencarian tanpa match mengembalikan 0.
- Sort pada array 0 atau 1 elemen tidak menyebabkan crash (cek guard
count < 2). - Sort by name memakai
strcmp(case-sensitive) — “Apel” lebih dulu dari “apel” karena ASCII ‘A’ < ‘a’. Bila ingin case-insensitive sort, gantistrcmpdenganstrcasecmp(POSIX) atau implementasi sendiri.
gcc -std=c11 -Wall -Wextra -o /tmp/demo_search demo_search.c \
inventory.c product.c -lm
/tmp/demo_search