whereNull Method
The whereNull()
method filters the collection to include only items where the specified key's value is null
or undefined
. This is particularly useful for finding incomplete records or handling optional fields.
Basic Syntax
typescript
collect(items).whereNull(key: keyof T): Collection<T>
Examples
Basic Usage
typescript
import { collect } from 'ts-collect'
// Simple null check
const items = collect([
{ id: 1, name: 'Widget', description: null },
{ id: 2, name: 'Gadget', description: 'A cool gadget' },
{ id: 3, name: 'Tool', description: null }
])
const missingDescriptions = items.whereNull('description')
console.log(missingDescriptions.all())
// [
// { id: 1, name: 'Widget', description: null },
// { id: 3, name: 'Tool', description: null }
// ]
Working with Objects
typescript
interface Product {
id: number
name: string
lastSold: Date | null
lastRestocked: Date | null
category: string | null
}
const products = collect<Product>([
{ id: 1, name: 'Widget', lastSold: new Date(), lastRestocked: null, category: 'tools' },
{ id: 2, name: 'Gadget', lastSold: null, lastRestocked: new Date(), category: null },
{ id: 3, name: 'Tool', lastSold: null, lastRestocked: null, category: 'tools' }
])
// Find products never sold
const neverSold = products.whereNull('lastSold')
// Find products never restocked
const neverRestocked = products.whereNull('lastRestocked')
// Find uncategorized products
const uncategorized = products.whereNull('category')
Real-world Examples
Product Data Quality Checker
typescript
interface ProductRecord {
sku: string
name: string
description: string | null
price: number | null
category: string | null
imageUrl: string | null
}
class ProductDataChecker {
constructor(private products: Collection<ProductRecord>) {}
getIncompleteListings(): {
missingDescriptions: Collection<ProductRecord>,
missingPrices: Collection<ProductRecord>,
missingCategories: Collection<ProductRecord>,
missingImages: Collection<ProductRecord>
} {
return {
missingDescriptions: this.products.whereNull('description'),
missingPrices: this.products.whereNull('price'),
missingCategories: this.products.whereNull('category'),
missingImages: this.products.whereNull('imageUrl')
}
}
getCompletionStatus(): Map<string, number> {
const total = this.products.count()
return new Map([
['descriptions', (total - this.products.whereNull('description').count()) / total * 100],
['prices', (total - this.products.whereNull('price').count()) / total * 100],
['categories', (total - this.products.whereNull('category').count()) / total * 100],
['images', (total - this.products.whereNull('imageUrl').count()) / total * 100]
])
}
getNeedingAttention(): string[] {
return this.products
.filter(product =>
[
product.description,
product.price,
product.category,
product.imageUrl
].filter(value => value === null).length >= 2
)
.pluck('sku')
.toArray()
}
}
Order Processing System
typescript
interface Order {
id: string
customerId: string
shippingAddress: string | null
processedAt: Date | null
shippedAt: Date | null
deliveredAt: Date | null
}
class OrderProcessor {
constructor(private orders: Collection<Order>) {}
getUnprocessedOrders(): Collection<Order> {
return this.orders.whereNull('processedAt')
}
getMissingShippingInfo(): Collection<Order> {
return this.orders
.whereNull('shippingAddress')
.where('processedAt', null)
}
getShippingStatus(): {
awaitingShipment: Collection<Order>,
inTransit: Collection<Order>,
awaitingDelivery: Collection<Order>
} {
return {
awaitingShipment: this.orders
.whereNotNull('processedAt')
.whereNull('shippedAt'),
inTransit: this.orders
.whereNotNull('shippedAt')
.whereNull('deliveredAt'),
awaitingDelivery: this.orders
.whereNull('deliveredAt')
}
}
}
Advanced Usage
Customer Profile Analyzer
typescript
interface CustomerProfile {
id: string
email: string
phoneNumber: string | null
birthday: Date | null
preferences: string[] | null
lastLoginAt: Date | null
}
class ProfileAnalyzer {
constructor(private profiles: Collection<CustomerProfile>) {}
getIncompleteProfiles(): {
missingPhone: Collection<CustomerProfile>,
missingBirthday: Collection<CustomerProfile>,
missingPreferences: Collection<CustomerProfile>
} {
return {
missingPhone: this.profiles.whereNull('phoneNumber'),
missingBirthday: this.profiles.whereNull('birthday'),
missingPreferences: this.profiles.whereNull('preferences')
}
}
getInactiveUsers(days: number): Collection<CustomerProfile> {
const cutoff = new Date()
cutoff.setDate(cutoff.getDate() - days)
return this.profiles
.filter(profile =>
profile.lastLoginAt === null ||
profile.lastLoginAt < cutoff
)
}
getProfileCompleteness(): Map<string, {
complete: number,
incomplete: number,
percentage: number
}> {
const total = this.profiles.count()
const fields = ['phoneNumber', 'birthday', 'preferences'] as const
return new Map(
fields.map(field => [
field,
{
complete: total - this.profiles.whereNull(field).count(),
incomplete: this.profiles.whereNull(field).count(),
percentage: (total - this.profiles.whereNull(field).count()) / total * 100
}
])
)
}
}
Type Safety
typescript
interface TypedProduct {
id: number
name: string
description: string | null
category: string | null
}
const products = collect<TypedProduct>([
{ id: 1, name: 'A', description: null, category: 'X' },
{ id: 2, name: 'B', description: 'Test', category: null }
])
// Type-safe null checks
const noDescription = products.whereNull('description')
const noCategory = products.whereNull('category')
// TypeScript enforces valid keys and nullable types
// products.whereNull('name') // ✗ TypeScript error for non-nullable field
// products.whereNull('invalid') // ✗ TypeScript error for invalid key
Return Value
- Returns a new Collection containing only items where the specified key is null
- Original collection remains unchanged
- Maintains type safety with TypeScript
- Can be chained with other collection methods
- Considers both null and undefined as null values
- Empty collection if no matches found
Common Use Cases
1. Data Quality
- Missing information
- Incomplete records
- Required fields
- Data validation
- Quality metrics
2. Order Processing
- Missing addresses
- Incomplete orders
- Processing status
- Shipping details
- Payment validation
3. Product Management
- Incomplete listings
- Missing details
- Required attributes
- Image validation
- Price verification
4. Customer Profiles
- Incomplete profiles
- Missing contact info
- Required preferences
- Profile completion
- Data enrichment
5. Inventory Management
- Missing stock levels
- Incomplete records
- Required fields
- Location data
- Supplier information
6. Marketing Analysis
- Missing segments
- Incomplete campaigns
- Required metrics
- Attribution data
- Response tracking
7. Shipping Management
- Missing addresses
- Incomplete details
- Required documentation
- Tracking information
- Delivery status
8. Content Management
- Missing metadata
- Incomplete descriptions
- Required fields
- Media assets
- Category information
9. User Management
- Incomplete profiles
- Missing permissions
- Required settings
- Account details
- Contact information
10. Analytics
- Missing metrics
- Incomplete data
- Required fields
- Tracking gaps
- Report validation