Christopher DuBois
Christopher DuBois

Reputation: 43420

Finding All Positions for Multiple Elements in a Vector

Suppose I have the following vector:

x <- c(8, 6, 9, 9, 7, 3, 2, 5, 5, 1, 6, 8, 5, 2, 9, 3, 5, 10, 8, 2)

How can I find which elements are either 8 or 9?

Upvotes: 25

Views: 45894

Answers (7)

Debjyoti
Debjyoti

Reputation: 177

If you want to find the answer using loops, then the following script will do the job:

> req_nos<- c(8,9)
> pos<-list()
> for (i in 1:length(req_nos)){
  pos[[i]]<-which(x==req_nos[i])}

The output will look like this:

>pos
[[1]]
[1] 1 12 19
[[2]] 
[1] 3  4 15

Here, pos[[1]] contains positions of 8 and pos[[2]] contains positions of 9. If you are using the %in% method and change the input order of elements, i.e, c(9,8) instead of c(8,9), the output will be the same for both of them. This method alleviates such problem.

Upvotes: 1

user3474009
user3474009

Reputation: 311

Here is a generalized solution to find the locations of all target values (only works for vectors and 1-dimensional arrays).

locate <- function(x, targets) {
    results <- lapply(targets, function(target) which(x == target))
    names(results) <- targets
    results
}

This function returns a list because each target may have any number of matches, including zero. The list is sorted (and named) in the original order of the targets.

Here is an example in use:

sequence <- c(1:10, 1:10)

locate(sequence, c(2,9))
$`2`
[1]  2 12

$`9`
[1]  9 19

Upvotes: 2

Jaap
Jaap

Reputation: 83215

In this specific case you could also use grep:

# option 1
grep('[89]',x)
# option 2
grep('8|9',x)

which both give:

[1]  1  3  4 12 15 19

When you also want to detect number with more than one digit, the second option is preferred:

> grep('10|8',x)
[1]  1 12 18 19

However, I did put emphasis on this specific case at the start of my answer for a reason. As @DavidArenburg mentioned, this could lead to unintended results. Using for example grep('1|8',x) will detect both 1 and 10:

> grep('1|8',x)
[1]  1 10 12 18 19

In order to avoid that side-effect, you will have to wrap the numbers to be detected in word-bounderies:

> grep('\\b1\\b|8',x)
[1]  1 10 12 19

Now, the 10 isn't detected.

Upvotes: 2

mdsumner
mdsumner

Reputation:

You could try the | operator for short conditions

which(x == 8 | x == 9)

Upvotes: 11

andrewj
andrewj

Reputation: 3035

grepl maybe a useful function. Note that grepl appears in versions of R 2.9.0 and later. What's handy about grepl is that it returns a logical vector of the same length as x.

grepl(8, x)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE

grepl(9, x)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE

To arrive at your answer, you could do the following

grepl(8,x) | grepl(9,x)

Upvotes: -1

Yann Abraham
Yann Abraham

Reputation: 330

Alternatively, if you do not need to use the indices but just the elements you can do

> x <- sample(1:10,20,replace=TRUE)
> x
 [1]  6  4  7  2  9  3  3  5  4  7  2  1  4  9  1  6 10  4  3 10
> x[8<=x & x<=9]
[1] 9 9

Upvotes: 1

Christopher DuBois
Christopher DuBois

Reputation: 43420

This is one way to do it. First I get the indices at which x is either 8 or 9. Then we can verify that at those indices, x is indeed 8 and 9.

> inds <- which(x %in% c(8,9))
> inds
[1]  1  3  4 12 15 19
> x[inds]
[1] 8 9 9 8 9 8

Upvotes: 42

Related Questions