# List Comprehensions in R

Package provides Python-style list comprehensions for R. List comprehension expressions use usual loops (`for`, `while` and `repeat`) and usual `if` as list producers. Syntax is very similar to Python. The difference is that returned value should be at the end of the loop body.

There are three main functions:

- `to_list` converts usual R loops expressions to list producers. Expression should be started with `for`, `while` or `repeat`. You can iterate over multiple lists if you provide several loop variables in backticks. See examples.
- `to_vec` is the same as `to_list` but return vector. See examples.
- `alter` return the same type as its argument but with modified elements. It is useful for altering existing data.frames or lists. See examples.

Rather unpractical example - squares of even numbers:

    library(comprehenr)
    to_vec(for(i in 1:10) if(i %% 2==0) i*i)
    #> [1]   4  16  36  64 100

Pythagorean triples:

    to_list(for (x in 1:20) for (y in x:20) for (z in y:20) if (x^2 + y^2 == z^2) c(x, y, z))
    #> [[1]]
    #> [1] 3 4 5
    #>
    #> [[2]]
    #> [1]  5 12 13
    #>
    #> [[3]]
    #> [1]  6  8 10
    #>
    #> [[4]]
    #> [1]  8 15 17
    #>
    #> [[5]]
    #> [1]  9 12 15
    #>
    #> [[6]]
    #> [1] 12 16 20

More examples:

    colours = c("red", "green", "yellow", "blue")
    things = c("house", "car", "tree")
    to_vec(for(x in colours) for(y in things) paste(x, y))
    #>  [1] "red house"    "red car"      "red tree"     "green house"  "green car"
    #>  [6] "green tree"   "yellow house" "yellow car"   "yellow tree"  "blue house"
    #> [11] "blue car"     "blue tree"

    # prime numbers
    noprimes = to_vec(for (i in 2:7) for (j in seq(i*2, 99, i)) j)
    primes = to_vec(for (x in 2:99) if(!x %in% noprimes) x)
    primes
    #>  [1]  2  3  5  7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

You can iterate over multiple lists if you provide several loop variables in backticks:

    to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))
    #>  [1] "2 b"  "4 d"  "6 f"  "8 h"  "10 j" "12 l" "14 n" "16 p" "18 r" "20 t"
    #> [11] "22 v" "24 x" "26 z"

    set.seed(123)
    rand_sequence = runif(20)
    # gives only locally increasing values
    to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)
    #>  [1] 0.7883051 0.8830174 0.9404673 0.5281055 0.8924190 0.9568333 0.6775706
    #>  [8] 0.8998250 0.3279207 0.9545036

`alter` examples:

    data(iris)
    # scale numeric variables
    res = alter(for(i in iris) if(is.numeric(i)) scale(i))
    str(res)
    #> 'data.frame':    150 obs. of  5 variables:
    #>  $ Sepal.Length: num [1:150, 1] -0.898 -1.139 -1.381 -1.501 -1.018 ...
    #>   ..- attr(*, "scaled:center")= num 5.84
    #>   ..- attr(*, "scaled:scale")= num 0.828
    #>  $ Sepal.Width : num [1:150, 1] 1.0156 -0.1315 0.3273 0.0979 1.245 ...
    #>   ..- attr(*, "scaled:center")= num 3.06
    #>   ..- attr(*, "scaled:scale")= num 0.436
    #>  $ Petal.Length: num [1:150, 1] -1.34 -1.34 -1.39 -1.28 -1.34 ...
    #>   ..- attr(*, "scaled:center")= num 3.76
    #>   ..- attr(*, "scaled:scale")= num 1.77
    #>  $ Petal.Width : num [1:150, 1] -1.31 -1.31 -1.31 -1.31 -1.31 ...
    #>   ..- attr(*, "scaled:center")= num 1.2
    #>   ..- attr(*, "scaled:scale")= num 0.762
    #>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

    # convert factors to characters
    res = alter(for(i in iris) if(is.factor(i)) as.character(i))
    str(res)
    #> 'data.frame':    150 obs. of  5 variables:
    #>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
    #>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
    #>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
    #>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
    #>  $ Species     : chr  "setosa" "setosa" "setosa" "setosa" ...

    # drop factors
    res = alter(for(i in iris) if(is.factor(i)) exclude())
    str(res)
    #> 'data.frame':    150 obs. of  4 variables:
    #>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
    #>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
    #>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
    #>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...

    # 'data' argument example
    # specify which columns to map with a numeric vector of positions:
    res = alter(
        for(`i, value` in numerate(mtcars)) if(i %in% c(1, 4, 5)) as.character(value),
        data = mtcars
    )
    str(res)
    #> 'data.frame':    32 obs. of  11 variables:
    #>  $ mpg : chr  "21" "21" "22.8" "21.4" ...
    #>  $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
    #>  $ disp: num  160 160 108 258 360 ...
    #>  $ hp  : chr  "110" "110" "93" "110" ...
    #>  $ drat: chr  "3.9" "3.9" "3.85" "3.08" ...
    #>  $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
    #>  $ qsec: num  16.5 17 18.6 19.4 17 ...
    #>  $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
    #>  $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
    #>  $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
    #>  $ carb: num  4 4 1 1 2 1 4 2 2 4 ...

    # or with a vector of names:
    res = alter(
        for(`name, value` in mark(mtcars)) if(name %in% c("cyl", "am")) as.character(value),
        data = mtcars
    )
    str(res)
    #> 'data.frame':    32 obs. of  11 variables:
    #>  $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
    #>  $ cyl : chr  "6" "6" "4" "6" ...
    #>  $ disp: num  160 160 108 258 360 ...
    #>  $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
    #>  $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
    #>  $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
    #>  $ qsec: num  16.5 17 18.6 19.4 17 ...
    #>  $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
    #>  $ am  : chr  "1" "1" "1" "0" ...
    #>  $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
    #>  $ carb: num  4 4 1 1 2 1 4 2 2 4 ...