Replace empty element in a list using sapply - r

I have two lists. The first one has an empty element. I'd like to replace that empty element with the first vector of the third list element of another list.
l1 <- list(a=1:3,b=4:9,c="")
l2 <- list(aa=11:13,bb=14:19,cc=data.frame(matrix(100:103,ncol=2)))
l1[sapply(l1, `[[`, 1)==""] <- l2[[3]][[1]]
Using sapply, I can identify which elements are empty. However, when I try to assign a vector to this empty element: I get this error message:
Warning message: In l1[sapply(l1, [[, 1) == ""] <- l2[[3]][[1]] :
number of items to replace is not a multiple of replacement length
This is only a warning, but the result I get is not the one I want. This is the l1 I get:
> l1
$a
[1] 1 2 3
$b
[1] 4 5 6 7 8 9
$c
[1] 100
This is what I need (two elements in $c):
> l1
$a
[1] 1 2 3
$b
[1] 4 5 6 7 8 9
$c
[1] 100 101

Just use l2[[3]][1] on the right hand side (single [ not [[)

The right-hand side should be a list, since you're replacing a list element. So you want that to be
... <- list(l2[[3]][[1]])
In addition, you might consider using !nzchar(l1) in place of sapply(...) == "". It might be more efficient. The final expression would be:
l1[!nzchar(l1)] <- list(l2[[3]][[1]])
giving the updated l1:
$a
[1] 1 2 3
$b
[1] 4 5 6 7 8 9
$c
[1] 100 101

Related

R list get first item of each element

This should probably be very easy for someone to answer but I have had no success on finding the answer anywhere.
I am trying to return, from a list in R, the first item of each element of the list.
> a
[1] 1 2 3
> b
[1] 11 22 33
> c
[1] 111 222 333
> d <- list(a = a,b = b,c = c)
> d
$a
[1] 1 2 3
$b
[1] 11 22 33
$c
[1] 111 222 333
Based on the construction of my list d above, I want to return a vector with three values:
return 1 11 111
sapply(d, "[[", 1) should do the trick.
A bit of explanation:
sapply: iterates over the elements in the list
[[: is the subset function. So we are asking sapply to use the subset function on each list element.
1 : is an argument passed to "[["
It turns out that "[" or "[[" can be called in a traditional manner which may help to illustrate the point:
x <- 10:1
"["(x, 3)
# [1] 8
You can do
output <- sapply(d, function(x) x[1])
If you don't need the names
names(output) <- NULL

remove certain vectors from a list

I want to remove certain vectors from a list. I have for example this:
a<-c(1,2,5)
b<-c(1,1,1)
c<-c(1,2,3,4)
d<-c(1,2,3,4,5)
exampleList<-list(a,b,c,d)
exampleList returns of course:
[[1]]
[1] 1 2 5
[[2]]
[1] 1 1 1
[[3]]
[1] 1 2 3 4
[[4]]
[1] 1 2 3 4 5
Is there a way to remove certain vectors from a list in R. I want to remove all vectors in the list exampleList which contain both 1 and 5(so not only vectors which contain 1 or 5, but both). Thanks in advance!
Use Filter:
filteredList <- Filter(function(v) !(1 %in% v & 5 %in% v), exampleList)
print(filteredList)
#> [[1]]
#> [1] 1 1 1
#>
#> [[2]]
#> [1] 1 2 3 4
Filter uses a functional style. The first argument you pass is a function that returns TRUE for an element you want to keep in the list, and FALSE for an element you want to remove from the list. The second argument is just the list itself.
We can use sapply on every list element and remove those elements where both the values 1 and 5 are present.
exampleList[!sapply(exampleList, function(x) any(x == 1) & any(x == 5))]
#[[1]]
#[1] 1 1 1
#[[2]]
#[1] 1 2 3 4
Here a solution with two steps:
exampleList<-list(a=c(1,2,5), b=c(1,1,1), c=c(1,2,3,4), d=c(1,2,3,4,5))
L <- lapply(exampleList, function(x) if (!all(c(1,5) %in% x)) x)
L[!sapply(L, is.null)]
# $b
# [1] 1 1 1
#
# $c
# [1] 1 2 3 4
Here is a one-step variant without any definition of a new function
exampleList[!apply(sapply(exampleList, '%in%', x=c(1,5)), 2, all)]
(... but it has two calls to apply-functions)

Check if there is overlap between elements of a list

I have a list of integers and I want to check if the elements are all unique ones.
set.seed(2)
x <- list(a=sample(10,3),b=sample(10,5),c=sample(10,7))
x
# $a
# [1] 2 7 5
# $b
# [1] 2 9 8 1 6
# $c
# [1] 5 10 9 2 8 1 7
For this example, all of the following situations fails the check: 1) 2 appears in all entries, 2) 5 appears in $a and $c, 3) 8 appears in $b and $c, 4) 1 appears in $b and $c, etc.
y <- list(a=c(1,3,5),b=c(7,4),c=c(6,10))
There is no overlapping between elements of y, so it passes the check.
The expected output should be just True/False indicating whether the list passes the check.
You can convert the list to a vector with unlist and then check if any elements are duplicated in the vector with any and duplicated.
!any(duplicated(unlist(x)))
# [1] FALSE
!any(duplicated(unlist(y)))
# [1] TRUE

Why are these sequences reversed when generated with the colon operator?

I've noticed that when I try to generate a list of sequences with the : operator (without an anonymous function), the sequences are always reversed. Take the following example.
x <- c(4, 6, 3)
lapply(x, ":", from = 1)
# [[1]]
# [1] 4 3 2 1
#
# [[2]]
# [1] 6 5 4 3 2 1
#
# [[3]]
# [1] 3 2 1
But when I use seq, everything is fine.
lapply(x, seq, from = 1)
# [[1]]
# [1] 1 2 3 4
#
# [[2]]
# [1] 1 2 3 4 5 6
#
# [[3]]
# [1] 1 2 3
And from help(":") it is stated that
For other arguments from:to is equivalent to seq(from, to), and generates a sequence from from to to in steps of 1 or -1.
Why is the first list of sequences reversed?
Can I generated forward sequences this way with the colon operator with lapply?
Or do I always have to use lapply(x, function(y) 1:y)?
The ":" operator is implemented as the primitive do_colon function in C. This primitive function does not have named arguments. It simply takes the first parameter as the "from" and the second as the "to" ignorning any parameter names. See
`:`(to=10, from=5)
# [1] 10 9 8 7 6 5
Additionally the lapply function only passes it's values as a leading unnamed parameter in the function call. You cannot pass values to primitive functions via lapply as the second positional argument.

Naming list items via loop in R

I want to take a list, create a String vector of the names of the list items, filling in blanks with a generic name and then set the names vector as the names of the list.
My code works fine for list who dont have items with names in it. It however does nothing, when there are items with a name in it.
addNamesToList <- function(myList){
listNames <- vector()
for(i in 1:length(myList)){
if(identical(names(myList[i]),NULL)){
listNames <- c(listNames,paste("item",i,sep=""))
}else{
listNames <- c(listNames,names(myList[i]))
}
}
names(myList) <- listNames
return (myList)
}
result without named items
$item1
[1] 2 3 4
$item2
[1] "hey" "ho"
result with named items
[[1]]
[1] 2 3 4
[[2]]
[1] "hey" "ho"
$hello
[1] 2 3 4
Hope you can help.
It sounds like you want to insert names where there is not currently a name. If that's the case, I would suggest using direct assignment via names(x) <- value, instead of using a loop to fill in the blanks.
In the following example, lst creates a sample list of three elements, the second of which is not named. Notice that even if only one of the list element has a name, its names vector is a character vector the same length as lst.
( lst <- list(item1 = 1:5, 6:10, item3 = 11:15) )
# $item1
# [1] 1 2 3 4 5
#
# [[2]]
# [1] 6 7 8 9 10
#
# $item3
# [1] 11 12 13 14 15
names(lst)
# [1] "item1" "" "item3"
We can insert a name into the empty name element with the following. This will also work with a vector, provided the right side vector is the same length as the left side vector.
names(lst)[2] <- "item2"
lst
# $item1
# [1] 1 2 3 4 5
#
# $item2
# [1] 6 7 8 9 10
#
# $item3
# [1] 11 12 13 14 15
For a longer list containing sporadic empty names, you can use
names(list)[!nzchar(names(list))] <- namesToAdd
nzchar basically means "non-zero character" and returns a logical, TRUE if the element is a non-zero length string.

Resources