0

How can I write a function that returns odd numbers only from a list of integers, using R language? So far I tried this

 function3 <- function(x){
      x<-as.integer(x)
      if (x %% 2 ==1) {
          return(x)
      }
    }

but it doesn't work for lists or vectors, and I don't really know how to change my code since the "if" will complain with the condition >"has length > 1" and only the first element will be used

  • 1
    `function(x) x[ x %% 2 == 1 ]`. Some problems with your code: if `x` is a vector then your `if` will complain with `the condition has length > 1 and only the first element will be used`, and since you do not cut out the non-odd elements, it returns the input argument. (The only change is that I'm inferring you intend it to be integerized, though you would really have needed `x <- as.integer(x)` for that.) – r2evans Mar 09 '19 at 19:11
  • Thank you very much , it was a typo, and that explains the first error message. Now that my code work for single integer; I would like to use lists (vectors) and have my function returns only the odd elements of the list but I don' really know how to do so. – Lakhdar Mohamed Amine Mar 09 '19 at 19:15
  • 2
    My comment has the answer in the first 28 characters. The only thing you need to add to it is if you need to convert to integers. – r2evans Mar 09 '19 at 19:23
  • At least part of this question is a duplicate of https://stackoverflow.com/q/23316161/3358272 and https://stackoverflow.com/q/14170778/3358272. Your original question of how to return odd numbers was not enough to know it was a duplicate the first time. – r2evans Mar 09 '19 at 19:35

1 Answers1

2
justodd <- function(x) x[ x %% 2 == 1 ]
justodd(4:20)
# [1]  5  7  9 11 13 15 17 19

Explanation:

  • Indexing is described in ?Extract (also in ?[), you'll see that it takes either a list of integer (or integerizable numeric) or logical. If the former, the numbers need to be within the length of the vector; if the latter, then it should be the same length as the original vector. For example,

    x <- 4:20
    x[c(3,5,1)]
    # [1] 6 8 4
    x[c(F,F,T,F,T,F,F,F,T,F,F,F,F,F,F,F,F)]
    # [1]  6  8 12
    
  • so our [-insides looks like

    x %% 2 == 1
    #  [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
    # [13] FALSE  TRUE FALSE  TRUE FALSE
    
  • then we index the original vector x based on that return value


Update

Your mention of it not working with a list as an argument suggests an alternative that is slower but works on both vectors and lists.

justodd2 <- function(x) Filter(function(a) a %% 2 == 1, x)
vec <- 4:20
lst <- as.list(4:20)
str(vec)
#  int [1:17] 4 5 6 7 8 9 10 11 12 13 ...
str(lst)
# List of 17
#  $ : int 4
#  $ : int 5
#  $ : int 6
#  $ : int 7
#  $ : int 8
#  $ : int 9
#  $ : int 10
#  $ : int 11
#  $ : int 12
#  $ : int 13
#  $ : int 14
#  $ : int 15
#  $ : int 16
#  $ : int 17
#  $ : int 18
#  $ : int 19
#  $ : int 20
justodd2(vec)
# [1]  5  7  9 11 13 15 17 19
justodd2(lst)
# [[1]]
# [1] 5
# [[2]]
# [1] 7
# [[3]]
# [1] 9
# [[4]]
# [1] 11
# [[5]]
# [1] 13
# [[6]]
# [1] 15
# [[7]]
# [1] 17
# [[8]]
# [1] 19

Performance comparison:

microbenchmark::microbenchmark(
  a = justodd(vec),
  b = justodd2(vec),
  c = justodd2(lst)
)
# Unit: nanoseconds
#  expr   min    lq  mean median    uq     max neval
#     a   800  1000 26642   1100  1200 2537700   100
#     b 12100 12500 24154  12700 13150 1055600   100
#     c 12100 12300 23777  12500 12800 1022900   100

(Ignore the high mean and max values from microbenchmark, they are often biased by garbage-collection in the R internals. If you don't know what that is ... just press the [I believe] button or google it.)

So ultimately, if you are always dealing with vectors, then I suggest the first justodd solution, otherwise the justodd2 is safer (as justodd(lst) fails).

r2evans
  • 141,215
  • 6
  • 77
  • 149