user=> (def xs [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20])
#'user/xs
user=> xs
[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
The task is to write a function that will drop, say, every fourth item and return the resulting sequence.
My first thought is that we can start by partitioning the sequence up into sub-sequences each, say, 4 elements, with the built-in function partition:-
user=> (partition 4 xs)
((1 2 3 4) (5 6 7 8) (9 10 11 12) (13 14 15 16) (17 18 19 20))
Then we take only the first 3 of each of these sub-sequences, thereby
omitting each fourth item overall:-
user=> (map #(take 3 %) *1)
((1 2 3) (5 6 7) (9 10 11) (13 14 15) (17 18 19))
Then we flatten these to make the whole list without the fourth ones:-
user=> (flatten *1)
(1 2 3 5 6 7 9 10 11 13 14 15 17 18 19)
However, this does not work for the general case. The function partition returns sub-sequences only until there are enough elements left in the list to have a whole sub-sequence. So 20 elements will work if you break into sub-sequences of four elements:-
user=> (partition 4 xs)
((1 2 3 4) (5 6 7 8) (9 10 11 12) (13 14 15 16) (17 18 19 20))
But not if I split by, say, six:-
user=> (partition 6 xs)
((1 2 3 4 5 6) (7 8 9 10 11 12) (13 14 15 16 17 18))
We lose 19 and 20: but if we were dropping every sixth item we would want these in. Hmm.
OK, plan B we use a list comprehension with for.
First, we want to number the elements of the list. Here is the sequence:-
user=> (for [x xs] x)
(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
To pick out certain ones, we number them by putting each one into a vector pair with successive numbers - provided by the function range:-
user=> (for [x (map vector (range) xs)] x)
([0 1] [1 2] [2 3] [3 4] [4 5] [5 6] [6 7] [7 8] [8 9] [9 10] [10 11] [11 12]
[12 13] [13 14] [14 15] [15 16] [16 17] [17 18] [18 19] [19 20])
We destructure the vectors into an element x and a count n, so our output can just show the element x:-
user=> (for [[n x] (map vector (range) xs)] x)
(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
I've confused matters by starting with a sequence that contains numbers in the first place. Well, never mind.
If I want to omit every fourth item then I want to drop the ones where n mod 4 is zero - that is, retain the ones where mod n mod 4 > 0. No, hang on - the counter n starts from zero not one - so I want to retain the ones where (n + 1) mod 4 > 0.
So I want a filter in my list comprehension (> (mod (+ n 1) 4) 0) like so:-
user=> (for [[n x] (map vector (range) xs) :when (> (mod (+ n 1) 4) 0)] x)
(1 2 3 5 6 7 9 10 11 13 14 15 17 18 19)
Does this work for every sixth item?
user=> (for [[n x] (map vector (range) xs) :when (> (mod (+ n 1) 6) 0)] x)
(1 2 3 4 5 7 8 9 10 11 13 14 15 16 17 19 20)