exercism

Exercism - Strain

This post shows you how to get Strain exercise of Exercism.

Stevinator Stevinator
7 min read
SHARE
exercism dart flutter strain

Preparation

Before we click on our next exercise, let’s see what concepts of DART we need to consider

Strain Exercise

So we need to use the following concepts.

Lists

Lists are ordered collections of items. You can iterate over lists, add elements, and create new lists.

void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  
  // Create empty list
  List<int> result = [];
  
  // Iterate and add elements
  for (var num in numbers) {
    if (num % 2 == 0) {
      result.add(num);
    }
  }
  
  print(result); // [2, 4]
}

Functions as Parameters

Functions can be passed as parameters to other functions. These are called higher-order functions. A predicate is a function that returns a boolean.

void main() {
  // Predicate function
  bool isEven(int x) => x % 2 == 0;
  
  // Function that takes a predicate
  List<int> filter(List<int> list, bool Function(int) predicate) {
    List<int> result = [];
    for (var item in list) {
      if (predicate(item)) {
        result.add(item);
      }
    }
    return result;
  }
  
  List<int> numbers = [1, 2, 3, 4, 5];
  List<int> evens = filter(numbers, isEven);
  print(evens); // [2, 4]
}

For-in Loops

For-in loops allow you to iterate through each element in a collection without needing to manage indices.

void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  
  // Iterate through each element
  for (var num in numbers) {
    print(num);
  }
  
  // With conditional
  for (var num in numbers) {
    if (num > 3) {
      print('Large: $num');
    }
  }
}

Conditional Logic

You can use if statements to check conditions and decide whether to include elements in the result.

void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  List<int> result = [];
  
  // Keep even numbers
  for (var num in numbers) {
    if (num % 2 == 0) {
      result.add(num);
    }
  }
  
  print(result); // [2, 4]
}

Boolean Logic

Boolean values (true/false) are returned by predicates. You can use logical operators to combine or negate conditions.

void main() {
  bool isEven(int x) => x % 2 == 0;
  
  int num = 4;
  bool result = isEven(num);
  print(result); // true
  
  // Negate the predicate
  bool isOdd(int x) => !isEven(x);
  print(isOdd(4)); // false
  print(isOdd(5)); // true
}

Generics

Generics allow you to write code that works with different types. The <T> syntax creates a type parameter that can be replaced with any type.

void main() {
  // Generic function that works with any type
  List<T> filter<T>(List<T> list, bool Function(T) predicate) {
    List<T> result = [];
    for (var item in list) {
      if (predicate(item)) {
        result.add(item);
      }
    }
    return result;
  }
  
  // Works with integers
  List<int> numbers = [1, 2, 3, 4, 5];
  List<int> evens = filter(numbers, (x) => x % 2 == 0);
  
  // Works with strings
  List<String> words = ["apple", "banana", "cherry"];
  List<String> longWords = filter(words, (w) => w.length > 5);
}

List Comprehensions (Collection For)

List comprehensions (also called collection for) provide a concise way to create lists by iterating and conditionally including elements. The syntax [for (item in collection) if (condition) item] creates a new list in a single expression.

void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  
  // List comprehension: keep even numbers
  List<int> evens = [for (final num in numbers) if (num % 2 == 0) num];
  print(evens); // [2, 4]
  
  // List comprehension: keep numbers greater than 3
  List<int> large = [for (final num in numbers) if (num > 3) num];
  print(large); // [4, 5]
  
  // Works with any type
  List<String> words = ["apple", "banana", "cherry"];
  List<String> longWords = [for (final word in words) if (word.length > 5) word];
  print(longWords); // [banana, cherry]
  
  // Can transform elements too
  List<int> doubled = [for (final num in numbers) num * 2];
  print(doubled); // [2, 4, 6, 8, 10]
}

Introduction

Implement the keep and discard operation on collections. Given a collection and a predicate on the collection’s elements, keep returns a new collection containing those elements where the predicate is true, while discard returns a new collection containing those elements where the predicate is false.

For example, given the collection of numbers:

1, 2, 3, 4, 5

And the predicate:

is the number even?

Then your keep operation should produce:

2, 4

While your discard operation should produce:

1, 3, 5

Note that the union of keep and discard is all the elements.

The functions may be called keep and discard, or they may need different names in order to not clash with existing functions or concepts in your language.

Restrictions

Keep your hands off that filter/reject/whatchamacallit functionality provided by your standard library! Solve this one yourself using other basic tools instead.

What is filtering?

Filtering is the process of selecting elements from a collection based on a condition (predicate). The keep operation selects elements where the predicate is true, while discard selects elements where the predicate is false.

This is a fundamental operation in functional programming and data processing. Many languages provide built-in filter functions, but implementing it manually helps understand the underlying logic.

— Functional Programming

How can we implement keep and discard?

To implement keep and discard manually:

  1. Keep: Iterate through the collection, check each element with the predicate, and include elements where the predicate returns true in the result
  2. Discard: Iterate through the collection, check each element with the predicate, and include elements where the predicate returns false in the result

The key insight is that discard is the opposite of keep - it keeps elements where the predicate is false.

We can use Dart’s list comprehensions (collection for) to create the filtered lists in a concise way. The syntax [for (value in collection) if (condition) value] allows us to build a new list by iterating and conditionally including elements.

For example, with [1, 2, 3, 4, 5] and predicate isEven:

  • Keep: [for (final num in numbers) if (isEven(num)) num][2, 4]
  • Discard: [for (final num in numbers) if (!isEven(num)) num][1, 3, 5]

Solution

class Strain {
  List<T> keep<T>(List<T> values, bool Function(T) predicate) =>
      [for (final value in values) if (predicate(value)) value];
  
  List<T> discard<T>(List<T> values, bool Function(T) predicate) =>
      [for (final value in values) if (!predicate(value)) value];
}

Let’s break down the solution:

  1. keep<T> - Keeps elements where the predicate is true:

    • List<T> values - The input collection (generic type T works with any type)
    • bool Function(T) predicate - A function that takes an element and returns a boolean
    • Uses a list comprehension: [for (final value in values) if (predicate(value)) value]
    • Iterates through each value in the collection
    • If predicate(value) returns true, includes the value in the result list
    • Returns the filtered list in a single, concise expression
  2. discard<T> - Discards elements where the predicate is true (keeps where it’s false):

    • Same structure as keep
    • Uses !predicate(value) to negate the predicate
    • Includes items where the predicate returns false
    • Returns elements that don’t match the predicate

The solution uses Dart’s list comprehensions (collection for) to manually implement filtering in a concise, functional style. This approach:

  • Creates a new list in a single expression
  • Iterates through the collection and conditionally includes elements
  • Works with any type thanks to generics
  • Avoids using Dart’s built-in filter method as required by the exercise

A video tutorial for this exercise is coming soon! In the meantime, check out my YouTube channel for more Dart and Flutter tutorials. 😉

Visit My YouTube Channel
Stevinator

Stevinator

Stevinator is a software engineer passionate about clean code and best practices. Loves sharing knowledge with the developer community.