362

I have a list and I want to remove a single element from it. How can I do this?

I've tried looking up what I think the obvious names for this function would be in the reference manual and I haven't found anything appropriate.

Kim
  • 4,080
  • 2
  • 30
  • 51
David Locke
  • 17,926
  • 9
  • 33
  • 53
  • Depends do you want to remove it by value e.g. "the value 5", or by index/indices "the element at index 5" or "at indices c(5:6,10)? If you want to remove by value and there are duplicates, then do you want to remove only the duplicates, first or last occurrence, or all? Is it guaranteed that the list contains your element/index? Do we need to handle the case where the list is empty? Do we need to ensure NA is passed (/excluded)? Is the list guaranteed to be flat or can it be nested? How many laters deep? – smci Aug 23 '18 at 00:37
  • 5
    setdiff(myList,elementToRemove) – JStrahl Sep 19 '18 at 13:48

19 Answers19

291

If you don't want to modify the list in-place (e.g. for passing the list with an element removed to a function), you can use indexing: negative indices mean "don't include this element".

x <- list("a", "b", "c", "d", "e")  # example list

x[-2]        # without 2nd element

x[-c(2, 3)]  # without 2nd and 3rd

Also, logical index vectors are useful:

x[x != "b"]  # without elements that are "b"

This works with dataframes, too:

df <- data.frame(number = 1:5, name = letters[1:5])

df[df$name != "b", ]      # rows without "b"

df[df$number %% 2 == 1, ] # rows with odd numbers only
Florian Jenn
  • 5,211
  • 4
  • 23
  • 18
  • 8
    Your logical index only works if you have that single item "b" in a list element. You cannot remove , say, `x$b` that way, nor can you remove "b" from a list element `x[[2]] = c("b","k")` . – Carl Witthoft Jan 31 '18 at 13:22
  • Regarding single vs multiple items: you could use `%in%` for testing against multiple items. I’m not sure what you mean by “cannot remove x$b” – do you mean removing the whole column `b`? – Florian Jenn Aug 17 '18 at 09:27
251

I don't know R at all, but a bit of creative googling led me here: http://tolstoy.newcastle.edu.au/R/help/05/04/1919.html

The key quote from there:

I do not find explicit documentation for R on how to remove elements from lists, but trial and error tells me

myList[[5]] <- NULL

will remove the 5th element and then "close up" the hole caused by deletion of that element. That suffles the index values, So I have to be careful in dropping elements. I must work from the back of the list to the front.

A response to that post later in the thread states:

For deleting an element of a list, see R FAQ 7.1

And the relevant section of the R FAQ says:

... Do not set x[i] or x[[i]] to NULL, because this will remove the corresponding component from the list.

Which seems to tell you (in a somewhat backwards way) how to remove an element.

starball
  • 20,030
  • 7
  • 43
  • 238
Chad Birch
  • 73,098
  • 23
  • 151
  • 149
  • 8
    Thanks, mylist[i] <- NULL is exactly the way to do it. – David Locke Mar 16 '09 at 22:15
  • 47
    This did not work for me. I get: `Error in list[length(list)] <- NULL : replacement has length zero` – wfbarksdale Oct 05 '11 at 02:39
  • 5
    @Aleksandr Levchuck 's post showed me that I was actually dealing with a vector and needed to create a new object – wfbarksdale Oct 05 '11 at 02:43
  • This is a great shortcut, but it seems to me like @Kim's answer using `within` would be the "right" way to remove list elements, since it allows the use of character strings to identify list elements, can remove multiple elements simultaneously, and does not need to be done in place. Am I missing something (other than the fact that the OP's question was about removing a single element)? Thanks. – Josh May 20 '21 at 21:17
65

I would like to add that if it's a named list you can simply use within.

l <- list(a = 1, b = 2)    
> within(l, rm(a))
$b
[1] 2

So you can overwrite the original list

l <- within(l, rm(a)) 

to remove element named a from list l.

Kim
  • 4,080
  • 2
  • 30
  • 51
40

Here is how the remove the last element of a list in R:

x <- list("a", "b", "c", "d", "e")
x[length(x)] <- NULL

If x might be a vector then you would need to create a new object:

x <- c("a", "b", "c", "d", "e")
x <- x[-length(x)]
  • Work for lists and vectors
Aleksandr Levchuk
  • 3,751
  • 4
  • 35
  • 47
  • @krlmlr: on the contrary, this solution is more general than Florian's answer, as it is polymorphic in the type of the collection. – Dan Barowy Aug 05 '14 at 15:23
  • @DanBarowy: I was wrong: This seems to be a synthesis of Chad's answer (the accepted one) and Florian's... A good brief summary, though. – krlmlr Aug 05 '14 at 18:12
23

Removing Null elements from a list in single line :

x=x[-(which(sapply(x,is.null),arr.ind=TRUE))]

Cheers

Sukhi
  • 826
  • 1
  • 8
  • 19
  • 3
    This code breaks when `x` is an empty list. Use `compact` from `plyr` for this task instead. – Richie Cotton Jan 08 '14 at 09:16
  • Also if there are no nulls in the list, `-(which(sapply(x,is.null),arr.ind=TRUE))` returns `named integer(0)` which will drop that row entirely. – user3055034 Sep 29 '15 at 18:42
19

If you have a named list and want to remove a specific element you can try:

lst <- list(a = 1:4, b = 4:8, c = 8:10)

if("b" %in% names(lst)) lst <- lst[ - which(names(lst) == "b")]

This will make a list lst with elements a, b, c. The second line removes element b after it checks that it exists (to avoid the problem @hjv mentioned).

or better:

lst$b <- NULL

This way it is not a problem to try to delete a non-existent element (e.g. lst$g <- NULL)

alko989
  • 7,688
  • 5
  • 39
  • 62
16

Use - (Negative sign) along with position of element, example if 3rd element is to be removed use it as your_list[-3]

Input

my_list <- list(a = 3, b = 3, c = 4, d = "Hello", e = NA)
my_list
# $`a`
# [1] 3

# $b
# [1] 3

# $c
# [1] 4

# $d
# [1] "Hello"

# $e
# [1] NA

Remove single element from list

 my_list[-3]
 # $`a`
 # [1] 3

 # $b
 # [1] 3

 # $d
 # [1] "Hello"

 # $e
 [1] NA

Remove multiple elements from list

 my_list[c(-1,-3,-2)]
 # $`d`
 # [1] "Hello"

 # $e
 # [1] NA

 my_list[c(-3:-5)]
 # $`a`
 # [1] 3

 # $b
 # [1] 3

 my_list[-seq(1:2)]
 # $`c`
 # [1] 4

 # $d
 # [1] "Hello"

 # $e
 # [1] NA
Sowmya S. Manian
  • 3,723
  • 3
  • 18
  • 30
11

There's the rlist package (http://cran.r-project.org/web/packages/rlist/index.html) to deal with various kinds of list operations.

Example (http://cran.r-project.org/web/packages/rlist/vignettes/Filtering.html):

library(rlist)
devs <- 
  list(
    p1=list(name="Ken",age=24,
      interest=c("reading","music","movies"),
      lang=list(r=2,csharp=4,python=3)),
    p2=list(name="James",age=25,
      interest=c("sports","music"),
      lang=list(r=3,java=2,cpp=5)),
    p3=list(name="Penny",age=24,
      interest=c("movies","reading"),
      lang=list(r=1,cpp=4,python=2)))

list.remove(devs, c("p1","p2"))

Results in:

# $p3
# $p3$name
# [1] "Penny"
# 
# $p3$age
# [1] 24
# 
# $p3$interest
# [1] "movies"  "reading"
# 
# $p3$lang
# $p3$lang$r
# [1] 1
# 
# $p3$lang$cpp
# [1] 4
# 
# $p3$lang$python
# [1] 2
user2030503
  • 3,064
  • 2
  • 36
  • 53
9

Don't know if you still need an answer to this but I found from my limited (3 weeks worth of self-teaching R) experience with R that, using the NULL assignment is actually wrong or sub-optimal especially if you're dynamically updating a list in something like a for-loop.

To be more precise, using

myList[[5]] <- NULL

will throw the error

myList[[5]] <- NULL : replacement has length zero

or

more elements supplied than there are to replace

What I found to work more consistently is

myList <- myList[[-5]]
tuomastik
  • 4,559
  • 5
  • 36
  • 48
user2035799
  • 99
  • 1
  • 2
  • 2
    Good answer! However, I think the `[[-5]]` should be single square brackets, otherwise you are deselecting only the contents of that list element, not the element itself. Well, at least using double square brackets gives me this error: "attempt to select more than one element". What works for me was then: `myList <- myList[-5]`. – n1k31t4 Jan 05 '16 at 18:02
7

Just wanted to quickly add (because I didn't see it in any of the answers) that, for a named list, you can also do l["name"] <- NULL. For example:

l <- list(a = 1, b = 2, cc = 3)
l['b'] <- NULL
Alexey Shiklomanov
  • 1,592
  • 13
  • 23
3

In the case of named lists I find those helper functions useful

member <- function(list,names){
    ## return the elements of the list with the input names
    member..names <- names(list)
    index <- which(member..names %in% names)
    list[index]    
}


exclude <- function(list,names){
     ## return the elements of the list not belonging to names
     member..names <- names(list)
     index <- which(!(member..names %in% names))
    list[index]    
}  
aa <- structure(list(a = 1:10, b = 4:5, fruits = c("apple", "orange"
)), .Names = c("a", "b", "fruits"))

> aa
## $a
##  [1]  1  2  3  4  5  6  7  8  9 10

## $b
## [1] 4 5

## $fruits
## [1] "apple"  "orange"


> member(aa,"fruits")
## $fruits
## [1] "apple"  "orange"


> exclude(aa,"fruits")
## $a
##  [1]  1  2  3  4  5  6  7  8  9 10

## $b
## [1] 4 5
DJJ
  • 2,481
  • 2
  • 28
  • 53
2

Using lapply and grep:

lst <- list(a = 1:4, b = 4:8, c = 8:10)
# say you want to remove a and c
toremove<-c("a","c")
lstnew<-lst[-unlist(lapply(toremove, function(x) grep(x, names(lst)) ) ) ]
# or
pattern<-"a|c"
lstnew<-lst[-grep(pattern, names(lst))]
# or
lst %>% purrr::discard(names(.) == "a") # use %in% for a set
Ferroao
  • 3,042
  • 28
  • 53
2

You can also negatively index from a list using the extract function of the magrittr package to remove a list item.

a <- seq(1,5)
b <- seq(2,6)
c <- seq(3,7)
l <- list(a,b,c)

library(magrittr)

extract(l,-1) #simple one-function method
[[1]]
[1] 2 3 4 5 6

[[2]]
[1] 3 4 5 6 7
Roasty247
  • 679
  • 5
  • 20
2

If you only want to remove the first occurrence of the element "b" and leave the rest

x <- c("a", "b", "b", "c", "d", "e")

which(x == "b")
# [1] 2 3

which(x == "b")[1]
# [1] 2

x[-which(x == "b")[1]]
# [1] "a" "b" "c" "d" "e"
Peter
  • 343
  • 5
  • 17
1

There are a few options in the purrr package that haven't been mentioned:

pluck and assign_in work well with nested values and you can access it using a combination of names and/or indices:

library(purrr)

l <- list("a" = 1:2, "b" = 3:4, "d" = list("e" = 5:6, "f" = 7:8))

# select values (by name and/or index)
all.equal(pluck(l, "d", "e"), pluck(l, 3, "e"), pluck(l, 3, 1))
[1] TRUE

# or if element location stored in a vector use !!!
pluck(l, !!! as.list(c("d", "e")))
[1] 5 6

# remove values (modifies in place)
pluck(l, "d", "e") <- NULL

# assign_in to remove values with name and/or index (does not modify in place)
assign_in(l, list("d", 1), NULL)
$a
[1] 1 2

$b
[1] 3 4

$d
$d$f
[1] 7 8

Or you can remove values using modify_list by assigning zap() or NULL:

all.equal(list_modify(l, a = zap()), list_modify(l, a = NULL))
[1] TRUE

You can remove or keep elements using a predicate function with discard and keep:

# remove numeric elements
discard(l, is.numeric)
$d
$d$e
[1] 5 6

$d$f
[1] 7 8

# keep numeric elements
keep(l, is.numeric)
$a
[1] 1 2

$b
[1] 3 4
LMc
  • 12,577
  • 3
  • 31
  • 43
  • `pluck(l, "d", "e") <- NULL` worked for me. I tried something like `names(list(a = 1, b = 2, c = 3)[c("b", "c")])` to get rid of `a`, but in my shiny app i got `NA, b, c`. Your `pluck` statement actually deleted the `a` value. Thanks! – RAFisherman Mar 28 '22 at 23:24
  • @captinbo I am not clear what you're trying to do. `names(list(a = 1, b = 2, c = 3)[c("b", "c")])` returns `"b" "c"`. If you're trying to remove `a` then you can assign it `NULL`, or select the other elements that are not `a`. – LMc Mar 29 '22 at 14:57
  • yes, it returns "b", "c", which is what I'm trying to do. But oddly, in my R Shiny app it was returning `"NA", "b", "c"` during runtime. I spent an hour on that bug trying different ways to subset my list to the elements I wanted, but only your `pluck` method worked. It was very odd that I was getting a list with the name `NA` in it. – RAFisherman Mar 29 '22 at 22:56
  • @captinbo this would be a good SO question if you could make a small reproducible example and post it. – LMc Mar 30 '22 at 16:39
0

Here is a simple solution that can be done using base R. It removes the number 5 from the original list of numbers. You can use the same method to remove whatever element you want from a list.

#the original list
original_list = c(1:10)

#the list element to remove
remove = 5

#the new list (which will not contain whatever the `remove` variable equals)
new_list = c()

#go through all the elements in the list and add them to the new list if they don't equal the `remove` variable
counter = 1
for (n in original_list){
  if (n != ){
    new_list[[counter]] = n
    counter = counter + 1
  }
}

The new_list variable no longer contains 5.

new_list
# [1]  1  2  3  4  6  7  8  9 10
arranjdavis
  • 657
  • 8
  • 16
-1

You can use which.

x<-c(1:5)
x
#[1] 1 2 3 4 5
x<-x[-which(x==4)]
x
#[1] 1 2 3 5
user2100721
  • 3,557
  • 2
  • 20
  • 29
Pavidus
  • 39
  • 1
-1

How about this? Again, using indices

> m <- c(1:5)
> m
[1] 1 2 3 4 5

> m[1:length(m)-1]
[1] 1 2 3 4

or

> m[-(length(m))]
[1] 1 2 3 4
C8H10N4O2
  • 18,312
  • 8
  • 98
  • 134
-1

if you'd like to avoid numeric indices, you can use

a <- setdiff(names(a),c("name1", ..., "namen"))

to delete names namea...namen from a. this works for lists

> l <- list(a=1,b=2)
> l[setdiff(names(l),"a")]
$b
[1] 2

as well as for vectors

> v <- c(a=1,b=2)
> v[setdiff(names(v),"a")]
b 
2
Greg Minshall
  • 547
  • 5
  • 6