Iterator Pattern: Every k-th Element
Problem
Write your own iterable class EveryKth that walks over an array and gives only the 1st, (k+1)th, (2k+1)th ... elements (indexes 0, k, 2k, ...). The caller must use a normal for-each loop (Iterable and Iterator in Java, Symbol.iterator in JavaScript, __iter__ in Python) and print the elements it receives, separated by spaces.
Constraints
- 1 <= k
- 1 <= n <= 10^5
Examples
Input
7 1 2 3 4 5 6 7 3
Output
1 4 7
Solutions
The Iterator pattern hides how a collection is walked. The caller only asks for the next element. Here the step logic lives in one class, so a different walk (reverse, filtered) can be added without changing the caller.
/*
* Problem : Every k-th element.
* Input : n, array, k.
* Output : Chosen elements.
* Approach: Custom iterator.
* Time : O(n / k)
* Space : O(n / k)
* Java : 8 or newer.
*/
import java.util.Scanner;
import java.util.Arrays;
import java.util.stream.Collectors;
public class Main {
// Step 1: Iterable has one method, iterator(). The for-each loop calls it for us.
static class EveryKth implements Iterable<Integer> {
private final int[] data;
private final int step;
EveryKth(int[] data, int step) {
this.data = data;
this.step = step;
}
@Override
public java.util.Iterator<Integer> iterator() {
// Step 2: The iterator remembers its own position. hasNext() and next() are all it needs.
return new java.util.Iterator<Integer>() {
private int position = 0;
@Override
public boolean hasNext() {
return position < data.length;
}
@Override
public Integer next() {
int value = data[position];
position += step;
return value;
}
};
}
}
// This method solves the task. It receives the input and returns the answer.
static int[] solve(int[] a, int k) {
// Step 3: The caller uses a normal for-each loop and knows nothing about the step logic.
java.util.List<Integer> picked = new java.util.ArrayList<>();
for (int value : new EveryKth(a, k)) {
picked.add(value);
}
int[] result = new int[picked.size()];
for (int i = 0; i < result.length; i++) {
result[i] = picked.get(i);
}
return result;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int size = scanner.nextInt();
int[] a = new int[size];
for (int i = 0; i < size; i++) {
a[i] = scanner.nextInt();
}
int k = scanner.nextInt();
System.out.println(Arrays.stream(solve(a, k)).mapToObj(String::valueOf).collect(Collectors.joining(" ")));
}
}
Hints (1)
- What does the for-each loop call behind the scenes?