I'm following this tutorial for an E-commerce website and applying the Sort functionality.
I've compared my code to the source code and I don't think there's difference in the method that the tutorial uses so I'm a little confused right now.
Here is the instructor's source code
Sorting the products by the price
field ascending & descending works. But somehow, it cannot sort the latest
and oldest
products by the createdAt
field.
If I do a console.log(a.createdAt - b.createdAt)
, it would return NaN
.
createdAt
and price
fields.a.createdAt
or b.createdAt
, it returns the createdAt
result as in the item.-> Question: How would we fix this problem or is there a better way to sort items based on created date in this case?
Product Schema
const mongoose = require('mongoose')
const ProductSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
desc: {
type: String,
required: true,
},
img: {
type: String,
required: true,
},
categories: {
type: Array,
required: true,
},
size: {
type: Array,
},
color: {
type: Array,
},
price: {
type: Number,
required: true,
},
inStock: {
type: Boolean,
default: true
}
},
{ timestamps: true }
)
module.exports = mongoose.model("Product", ProductSchema)
Products.js
const Products = ({ category, filters, sort }) => {
const [products, setProducts] = useState([])
const [filteredProducts, setFilteredProducts] = useState([])
useEffect(() => {
category && setFilteredProducts(
products.filter(item =>
Object.entries(filters).every(([key, value]) =>
item[key].includes(value)
)
)
)
}, [category, filters, products])
// Sorting:
useEffect(() => {
// Sort by latest date:
if (sort === "latest") {
setFilteredProducts(prev =>
[...prev].sort((a, b) =>
a.createdAt - b.createdAt
// console.log(a.createdAt - b.createdAt) -> This returns "NaN"
)
)
} else if (sort === "asc") {
setFilteredProducts(prev =>
[...prev].sort((a, b) => a.price - b.price)
)
} else if (sort === "desc") {
setFilteredProducts(prev =>
[...prev].sort((a, b) => b.price - a.price)
)
} else {
// Sort by oldest date:
setFilteredProducts(prev =>
[...prev].sort((a, b) => b.createdAt - a.createdAt)
)
}
}, [sort])
return (
<Container>
<Title>Popular In Store</Title>
<ProductsWrapper>
{filteredProducts.map(item => (
<Product key={item.id} item={item} />
))}
</ProductsWrapper>
</Container>
);
}
The createdAt
property is a non-number-like string, so attempting arithmetic operations on them will result in NaN
.
console.log("2022-04-03T07:55:18.556Z" - "2022-04-12T07:55:18.556Z"); // NaN
Use String.prototype.localeCompare()
to compare string values in a sort comparator.
console.log("2022-04-03T07:55:18.556Z".localeCompare("2022-04-12T07:55:18.556Z")); // -1
Example:
// Sorting:
useEffect(() => {
// Sort by latest date:
if (sort === "latest") {
setFilteredProducts(prev =>
[...prev].sort((a, b) => a.createdAt.localeCompare(b.createdAt)
))
} else if (sort === "asc") {
setFilteredProducts(prev =>
[...prev].sort((a, b) => a.price - b.price)
)
} else if (sort === "desc") {
setFilteredProducts(prev =>
[...prev].sort((a, b) => b.price - a.price)
)
} else {
// Sort by oldest date:
setFilteredProducts(prev =>
[...prev].sort((a, b) => b.createdAt.localeCompare(a.createdAt))
)
}
}, [sort]);
Note this only works when date strings use units in decreasing order of unit size, i.e. year, then month, then day, etc... So ensure your dates conform to this. If this is not practical then you'll need to resort to processing the date strings into DateTime objects and using other methods of comparison.
console.log(new Date("2022-04-03T07:55:18.556Z") - new Date("2022-04-12T07:55:18.556Z")); // -777600000