Question #413EasyDSA

Given a list of integers from 1 to 100, write functions to: (a) Return all even numbers, (b) Return all odd numbers, (c) Separate into two lists.

#algorithm#filtering#list#dart#functional-programming

Answer

Overview

Filtering even and odd numbers is a fundamental list operation that demonstrates filtering, partitioning, and Dart's functional programming features. This common task appears in data processing, algorithm problems, and interview questions.

Even number: Divisible by 2 (

text
n % 2 == 0
) Odd number: Not divisible by 2 (
text
n % 2 != 0
)


Approach 1: Using where() Method

Functional approach using Dart's built-in filtering.

dart
List<int> getEvenNumbers(List<int> numbers) {
  return numbers.where((n) => n % 2 == 0).toList();
}

List<int> getOddNumbers(List<int> numbers) {
  return numbers.where((n) => n % 2 != 0).toList();
}

void main() {
  var numbers = List.generate(10, (i) => i + 1); // [1, 2, 3, ..., 10]

  print(getEvenNumbers(numbers));  // [2, 4, 6, 8, 10]
  print(getOddNumbers(numbers));   // [1, 3, 5, 7, 9]
}

Time Complexity: O(n) Space Complexity: O(n) for result lists

Pros:

  • Clean and readable
  • Functional programming style
  • One-liner solution

Approach 2: Loop-Based Filtering

Traditional imperative approach.

dart
List<int> getEvenNumbersLoop(List<int> numbers) {
  List<int> evens = [];

  for (int n in numbers) {
    if (n % 2 == 0) {
      evens.add(n);
    }
  }

  return evens;
}

List<int> getOddNumbersLoop(List<int> numbers) {
  List<int> odds = [];

  for (int n in numbers) {
    if (n % 2 != 0) {
      odds.add(n);
    }
  }

  return odds;
}

void main() {
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  print(getEvenNumbersLoop(numbers));  // [2, 4, 6, 8, 10]
  print(getOddNumbersLoop(numbers));   // [1, 3, 5, 7, 9]
}

Time Complexity: O(n) Space Complexity: O(n)


Approach 3: Partition into Two Lists

Separate into even and odd in a single pass.

dart
Map<String, List<int>> partitionEvenOdd(List<int> numbers) {
  List<int> evens = [];
  List<int> odds = [];

  for (int n in numbers) {
    if (n % 2 == 0) {
      evens.add(n);
    } else {
      odds.add(n);
    }
  }

  return {'even': evens, 'odd': odds};
}

void main() {
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  var result = partitionEvenOdd(numbers);

  print(result['even']);  // [2, 4, 6, 8, 10]
  print(result['odd']);   // [1, 3, 5, 7, 9]
}

Advantage: Single iteration for both lists.


Approach 4: Using Records (Dart 3.0+)

Modern Dart syntax with records for cleaner return type.

dart
(List<int> evens, List<int> odds) partitionEvenOddRecord(List<int> numbers) {
  List<int> evens = [];
  List<int> odds = [];

  for (int n in numbers) {
    (n % 2 == 0 ? evens : odds).add(n);
  }

  return (evens, odds);
}

void main() {
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  var (evens, odds) = partitionEvenOddRecord(numbers);

  print('Evens: $evens');  // Evens: [2, 4, 6, 8, 10]
  print('Odds: $odds');    // Odds: [1, 3, 5, 7, 9]
}

Dart 3.0+ only - Uses record destructuring.


Approach 5: Custom Partition Function

Generic partition for any predicate.

dart
class Partition<T> {
  final List<T> matched;
  final List<T> unmatched;

  Partition(this.matched, this.unmatched);
}

Partition<T> partition<T>(List<T> items, bool Function(T) predicate) {
  List<T> matched = [];
  List<T> unmatched = [];

  for (T item in items) {
    (predicate(item) ? matched : unmatched).add(item);
  }

  return Partition(matched, unmatched);
}

void main() {
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  // Partition by even
  var result = partition(numbers, (n) => n % 2 == 0);
  print('Evens: ${result.matched}');      // [2, 4, 6, 8, 10]
  print('Odds: ${result.unmatched}');     // [1, 3, 5, 7, 9]

  // Works with any condition
  var byFive = partition(numbers, (n) => n >= 5);
  print('>=5: ${byFive.matched}');        // [5, 6, 7, 8, 9, 10]
  print('<5: ${byFive.unmatched}');       // [1, 2, 3, 4]
}

Complete Solution Class

dart
class EvenOddFilter {
  // Get even numbers
  static List<int> getEvens(List<int> numbers) {
    return numbers.where((n) => n % 2 == 0).toList();
  }

  // Get odd numbers
  static List<int> getOdds(List<int> numbers) {
    return numbers.where((n) => n % 2 != 0).toList();
  }

  // Partition in single pass
  static Map<String, List<int>> partition(List<int> numbers) {
    List<int> evens = [];
    List<int> odds = [];

    for (int n in numbers) {
      (n % 2 == 0 ? evens : odds).add(n);
    }

    return {'even': evens, 'odd': odds};
  }

  // Count evens and odds
  static Map<String, int> count(List<int> numbers) {
    int evens = 0, odds = 0;

    for (int n in numbers) {
      n % 2 == 0 ? evens++ : odds++;
    }

    return {'even': evens, 'odd': odds};
  }

  // Check if all even
  static bool allEven(List<int> numbers) {
    return numbers.every((n) => n % 2 == 0);
  }

  // Check if all odd
  static bool allOdd(List<int> numbers) {
    return numbers.every((n) => n % 2 != 0);
  }

  // Check if any even
  static bool anyEven(List<int> numbers) {
    return numbers.any((n) => n % 2 == 0);
  }

  // Sum of evens and odds
  static Map<String, int> sum(List<int> numbers) {
    int evenSum = 0, oddSum = 0;

    for (int n in numbers) {
      n % 2 == 0 ? evenSum += n : oddSum += n;
    }

    return {'evenSum': evenSum, 'oddSum': oddSum};
  }
}

void main() {
  var numbers = List.generate(10, (i) => i + 1);

  print(EvenOddFilter.getEvens(numbers));
  // [2, 4, 6, 8, 10]

  print(EvenOddFilter.partition(numbers));
  // {even: [2, 4, 6, 8, 10], odd: [1, 3, 5, 7, 9]}

  print(EvenOddFilter.count(numbers));
  // {even: 5, odd: 5}

  print(EvenOddFilter.sum(numbers));
  // {evenSum: 30, oddSum: 25}

  print(EvenOddFilter.allEven([2, 4, 6]));
  // true

  print(EvenOddFilter.anyEven([1, 3, 5, 6]));
  // true
}

Comparison

ApproachTimeSpaceReadabilityBest For
text
where()
O(n)O(n)⭐⭐⭐⭐⭐Production
Loop-basedO(n)O(n)⭐⭐⭐⭐Beginners
Partition (Map)O(n)O(n)⭐⭐⭐⭐Single pass
Partition (Record)O(n)O(n)⭐⭐⭐⭐⭐Dart 3.0+
GenericO(n)O(n)⭐⭐⭐Reusability

Generate Test Data

dart
void generateTestData() {
  // 1 to 100
  var range = List.generate(100, (i) => i + 1);

  print(getEvenNumbers(range).length);  // 50
  print(getOddNumbers(range).length);   // 50

  // Random numbers
  var random = List.generate(20, (_) => Random().nextInt(100));
  print(partitionEvenOdd(random));

  // Large dataset
  var large = List.generate(1000000, (i) => i);
  var sw = Stopwatch()..start();
  partitionEvenOdd(large);
  print('Time: ${sw.elapsedMilliseconds}ms');
}

Edge Cases

dart
void testEdgeCases() {
  // Empty list
  assert(getEvenNumbers([]).isEmpty);
  assert(getOddNumbers([]).isEmpty);

  // All even
  assert(getEvenNumbers([2, 4, 6]) == [2, 4, 6]);
  assert(getOddNumbers([2, 4, 6]).isEmpty);

  // All odd
  assert(getEvenNumbers([1, 3, 5]).isEmpty);
  assert(getOddNumbers([1, 3, 5]) == [1, 3, 5]);

  // Single element
  assert(getEvenNumbers([2]) == [2]);
  assert(getOddNumbers([3]) == [3]);

  // Zero (even by definition)
  assert(getEvenNumbers([0]) == [0]);

  // Negative numbers
  assert(getEvenNumbers([-2, -1, 0, 1, 2]) == [-2, 0, 2]);
  assert(getOddNumbers([-2, -1, 0, 1, 2]) == [-1, 1]);
}

Best Practices

1. Use Functional Methods

dart
// ✅ Clean and idiomatic
List<int> evens(List<int> nums) => nums.where((n) => n.isEven).toList();
List<int> odds(List<int> nums) => nums.where((n) => n.isOdd).toList();

2. Use Built-in isEven/isOdd

dart
// ✅ Dart provides these properties
n.isEven  // Better than n % 2 == 0
n.isOdd   // Better than n % 2 != 0

3. Partition for Efficiency

dart
// ✅ Single pass instead of two
var (evens, odds) = partition(numbers);

// ❌ Two passes
var evens = numbers.where((n) => n.isEven).toList();
var odds = numbers.where((n) => n.isOdd).toList();

4. Handle Null Safety

dart
List<int> getEvens(List<int>? numbers) {
  return numbers?.where((n) => n.isEven).toList() ?? [];
}

Real-World Applications

1. Split Data for Processing

dart
var (evens, odds) = partition(data);
processEvenData(evens);
processOddData(odds);

2. Alternate Row Styling

dart
Widget buildTable(List<Item> items) {
  return Column(
    children: items.asMap().entries.map((entry) {
      bool isEven = entry.key.isEven;
      return Container(
        color: isEven ? Colors.grey[100] : Colors.white,
        child: ItemRow(entry.value),
      );
    }).toList(),
  );
}

3. Distribute Tasks

dart
void distributeTasks(List<Task> tasks) {
  var (queue1, queue2) = partition(
    tasks.asMap().entries.toList(),
    (e) => e.key.isEven,
  );

  worker1.process(queue1.map((e) => e.value).toList());
  worker2.process(queue2.map((e) => e.value).toList());
}

Interview Tips

Common variations:

  1. "Filter even indices vs. even values?"

    • Clarify requirements
    • text
      list.asMap().entries.where((e) => e.key.isEven)
  2. "In-place filtering?"

    • text
      list.removeWhere((n) => n.isOdd)
      for evens only
  3. "Count instead of return?"

    • text
      numbers.where((n) => n.isEven).length
  4. "Performance with huge lists?"

    • Single-pass partition is best

Resources