Skip to content
Advertisement

React: Table custom sort doesn’t sort when data is mixed with capital and small letters

I am trying to sort data using custom sort function in react. Unfortunately the function only sort items if the records are either capital letter or small letter. It doesn’t sort the data with mixed capital and small letter properly. Here is the live link

Here is the code

const useSortableData = (items, config = null) => {
  const [sortConfig, setSortConfig] = React.useState(config);

  const sortedItems = React.useMemo(() => {
    let sortableItems = [...items];
    if (sortConfig !== null) {
      sortableItems.sort((a, b) => {
        if (a[sortConfig.key] < b[sortConfig.key]) {
          return sortConfig.direction === 'ascending' ? -1 : 1;
        }
        if (a[sortConfig.key] > b[sortConfig.key]) {
          return sortConfig.direction === 'ascending' ? 1 : -1;
        }
        return 0;
      });
    }
    return sortableItems;
  }, [items, sortConfig]);

  const requestSort = (key) => {
    let direction = 'ascending';
    if (
      sortConfig &&
      sortConfig.key === key &&
      sortConfig.direction === 'ascending'
    ) {
      direction = 'descending';
    }
    setSortConfig({ key, direction });
  };

  return { items: sortedItems, requestSort, sortConfig };
};

const ProductTable = (props) => {
  const { items, requestSort, sortConfig } = useSortableData(props.products);
  const getClassNamesFor = (name) => {
    if (!sortConfig) {
      return;
    }
    return sortConfig.key === name ? sortConfig.direction : undefined;
  };
  return (
    <table>
      <caption>Products</caption>
      <thead>
        <tr>
          <th>
            <button
              type="button"
              onClick={() => requestSort('name')}
              className={getClassNamesFor('name')}
            >
              Name
            </button>
          </th>
          <th>
            <button
              type="button"
              onClick={() => requestSort('price')}
              className={getClassNamesFor('price')}
            >
              Price
            </button>
          </th>
          <th>
            <button
              type="button"
              onClick={() => requestSort('stock')}
              className={getClassNamesFor('stock')}
            >
              In Stock
            </button>
          </th>
        </tr>
      </thead>
      <tbody>
        {items.map((item) => (
          <tr key={item.id}>
            <td>{item.name}</td>
            <td>${item.price}</td>
            <td>{item.stock}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

function App() {
  return (
    <div className="App">
      <ProductTable
        products={[
          { id: 1, name: 'Cheese', price: 4.9, stock: 20 },
          { id: 2, name: 'milk', price: 1.9, stock: 32 },
          { id: 3, name: 'Yoghurt', price: 2.4, stock: 12 },
          { id: 4, name: 'Heavy Cream', price: 3.9, stock: 9 },
          { id: 5, name: 'butter', price: 0.9, stock: 99 },
          { id: 6, name: 'Sour Cream ', price: 2.9, stock: 86 },
          { id: 7, name: 'Fancy French Cheese 🇫🇷', price: 99, stock: 12 },
        ]}
      />
    </div>
  );
}

ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
body {
  font-family: 'open sans', sans-serif;
  font-size: 16px;
}

table {
  width: 100%;
  border-collapse: collapse;
}

thead th {
  text-align: left;
  border-bottom: 2px solid black;
}

thead button {
  border: 0;
  border-radius: none;
  font-family: inherit;
  font-weight: 700;
  font-size: inherit;
  padding: 0.5em;
  margin-bottom: 1px;
}

thead button.ascending::after {
  content: '👇';
  display: inline-block;
  margin-left: 1em;
}

thead button.descending::after {
  content: '☝️';
  display: inline-block;
  margin-left: 1em;
}

tbody td {
  padding: 0.5em;
  border-bottom: 1px solid #ccc;
}

tbody tr:hover {
  background-color: #eee;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

I have tried to change a and b to lowercase from useSortable but since there is price column with number, I got error.

What can I do to make it work regardless of case sensitivity.

Advertisement

Answer

You can check for the type, like:

if (typeof a[sortConfig.key] === "string" && typeof b[sortConfig.key] === "string") {
    if (a[sortConfig.key].toLowerCase() < b[sortConfig.key].toLowerCase()) {
      return sortConfig.direction === 'ascending' ? -1 : 1;
    }
} else {
    if (a[sortConfig.key] < b[sortConfig.key]) {
      return sortConfig.direction === 'ascending' ? -1 : 1;
    }
}
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement