Search & Filter

Implement powerful search and filtering capabilities

Basic Search

Use the built-in search functions for countries and cities.

import { searchCountries, searchCities } from 'countries-cities-ar';

// Search countries by name
const egyptResults = searchCountries('egypt', 'en');
const arabResults = searchCountries('مصر', 'ar');

// Search cities globally
const cairoResults = searchCities('cairo');
const cities = cairoResults.map(result => ({
  city: result.city.name,
  country: result.country.name
}));

// Search cities within a specific country
const egyptianCities = searchCities('alex', 'EG', 'en');

Fuzzy Search

Implement fuzzy search for better user experience.

// Simple fuzzy search implementation
function fuzzySearch(query, text) {
  query = query.toLowerCase();
  text = text.toLowerCase();
  
  let queryIndex = 0;
  for (let i = 0; i < text.length && queryIndex < query.length; i++) {
    if (text[i] === query[queryIndex]) {
      queryIndex++;
    }
  }
  return queryIndex === query.length;
}

// Use with countries data
function searchCountriesFuzzy(query) {
  return allCountries.filter(country => 
    fuzzySearch(query, country.name) ||
    fuzzySearch(query, country.nameAr) ||
    fuzzySearch(query, country.nameFr)
  );
}

// Example: "egy" matches "Egypt"
const results = searchCountriesFuzzy('egy');

Advanced Filtering

Combine multiple filters for complex queries.

// Filter builder pattern
class CountryFilter {
  constructor(countries = allCountries) {
    this.countries = countries;
    this.filters = [];
  }
  
  withMinCities(min) {
    this.filters.push(c => c.cities.length >= min);
    return this;
  }
  
  withMaxCities(max) {
    this.filters.push(c => c.cities.length <= max);
    return this;
  }
  
  withArabicName() {
    this.filters.push(c => c.nameAr && c.nameAr.trim() !== '');
    return this;
  }
  
  byRegion(region) {
    const regionMap = {
      'africa': africaCountries,
      'asia': asiaCountries,
      'europe': europeCountries,
    };
    const regionCountries = regionMap[region] || [];
    const codes = regionCountries.map(c => c.code);
    this.filters.push(c => codes.includes(c.code));
    return this;
  }
  
  search(query, lang = 'en') {
    if (query) {
      this.filters.push(c => {
        const name = lang === 'ar' ? c.nameAr : 
                     lang === 'fr' ? c.nameFr : c.name;
        return name.toLowerCase().includes(query.toLowerCase());
      });
    }
    return this;
  }
  
  apply() {
    return this.countries.filter(country =>
      this.filters.every(filter => filter(country))
    );
  }
}

// Usage
const filtered = new CountryFilter()
  .withMinCities(10)
  .withArabicName()
  .byRegion('asia')
  .search('saudi', 'en')
  .apply();

Debounced Search Component

Optimize search performance with debouncing.

import { useState, useEffect, useCallback } from 'react';
import { searchCountries } from 'countries-cities-ar';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);
  
  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    
    return () => clearTimeout(handler);
  }, [value, delay]);
  
  return debouncedValue;
}

function SearchableCountryList() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [isSearching, setIsSearching] = useState(false);
  
  const debouncedQuery = useDebounce(query, 300);
  
  useEffect(() => {
    if (debouncedQuery) {
      setIsSearching(true);
      // Simulate async search
      setTimeout(() => {
        const searchResults = searchCountries(debouncedQuery, 'ar');
        setResults(searchResults);
        setIsSearching(false);
      }, 100);
    } else {
      setResults([]);
      setIsSearching(false);
    }
  }, [debouncedQuery]);
  
  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search countries..."
        className="w-full p-2 border rounded"
      />
      
      {isSearching && <div>Searching...</div>}
      
      <div className="grid gap-2 mt-4">
        {results.map(country => (
          <div key={country.code} className="p-2 border rounded">
            {country.nameAr} - {country.name}
          </div>
        ))}
      </div>
    </div>
  );
}

Sorting Results

Sort search results by relevance or other criteria.

// Sort by relevance score
function searchWithRelevance(query, lang = 'en') {
  const results = searchCountries(query, lang);
  
  return results.map(country => {
    const name = lang === 'ar' ? country.nameAr :
                 lang === 'fr' ? country.nameFr : country.name;
    
    // Calculate relevance score
    let score = 0;
    const lowerQuery = query.toLowerCase();
    const lowerName = name.toLowerCase();
    
    if (lowerName === lowerQuery) score = 100; // Exact match
    else if (lowerName.startsWith(lowerQuery)) score = 75; // Starts with
    else if (lowerName.includes(lowerQuery)) score = 50; // Contains
    
    // Bonus for shorter names (more likely to be relevant)
    score += Math.max(0, 20 - name.length);
    
    return { country, score };
  })
  .sort((a, b) => b.score - a.score)
  .map(item => item.country);
}

// Sort by multiple criteria
function sortCountries(countries, criteria) {
  return [...countries].sort((a, b) => {
    for (const { field, order = 'asc' } of criteria) {
      let aVal = a[field];
      let bVal = b[field];
      
      if (field === 'citiesCount') {
        aVal = a.cities.length;
        bVal = b.cities.length;
      }
      
      if (aVal < bVal) return order === 'asc' ? -1 : 1;
      if (aVal > bVal) return order === 'asc' ? 1 : -1;
    }
    return 0;
  });
}

// Usage
const sorted = sortCountries(allCountries, [
  { field: 'citiesCount', order: 'desc' }, // Most cities first
  { field: 'name', order: 'asc' }          // Then alphabetically
]);

Performance Tips

  • • Always debounce user input (300-500ms recommended)
  • • Limit result sets to improve rendering performance
  • • Use virtual scrolling for large result lists
  • • Consider indexing data for faster searches in large datasets
  • • Cache search results when appropriate

Countries Cities AR

Documentation

Version
v3.0.1
250 دولة • 4,642 محافظة