Reputation: 468
I have two lists that I want to compare. I want to see if the values of each element of the list are equal or not.
> m1
[[1]]
integer(0)
[[2]]
[1] 3 4
[[3]]
integer(0)
[[4]]
[1] 1
[[5]]
[1] 2 3 4
> m3
[[1]]
[1] 3
[[2]]
[1] 1 4
[[3]]
[1] 2
[[4]]
[1] 3
[[5]]
[1] 1 4
And I expect a result like this:
> Result
[[1]]
[1]
FALSE
[[2]]
[1]
FALSE TRUE
[[3]]
[1]
FALSE
[[4]]
[1]
FALSE
[[5]]
[1]
FALSE FALSE TRUE
If I try to apply m1[1] == m3[1]
or similar, I get messages as
Error in m1 == m3 : comparison of these types is not implemented.
I don't manage to do that simple thing! Thanks for your help in advance.
m1 <- list(integer(0), 3:4, integer(0), 1, 2:4)
m3 <- list(3, c(1, 4), 2, 3, c(1, 4))
Upvotes: 8
Views: 8809
Reputation: 2410
Expanding Darren's answer from above, one could also do:
any(unlist(Map(`%in%`, m1, m3)) == FALSE)
Upvotes: 0
Reputation: 35737
You can use Map
(or mapply
) with %in%
.
Map(`%in%`, m1, m3)
# [[1]]
# logical(0)
#
# [[2]]
# [1] FALSE TRUE
#
# [[3]]
# logical(0)
#
# [[4]]
# [1] FALSE
#
# [[5]]
# [1] FALSE FALSE TRUE
However, m1
contains integer(0)
, which makes %in%
return logical(0)
instead of FALSE
. So you need to convert logical(0)
to FALSE
afterward.
res <- Map(`%in%`, m1, m3)
res[lengths(res) == 0] <- FALSE
res
# [[1]]
# [1] FALSE
#
# [[2]]
# [1] FALSE TRUE
#
# [[3]]
# [1] FALSE
#
# [[4]]
# [1] FALSE
#
# [[5]]
# [1] FALSE FALSE TRUE
Another one-liner solution:
Map(\(x, y) x[seq_len(max(1, length(x)))] %in% y, m1, m3)
Note: integer(0)[1]
is NA
and NA %in% y
returns FALSE
.
Upvotes: 5
Reputation: 4053
The compare()
function in {waldo} makes it very easy to compare lists or data frames. In this case:
m1 = list(integer(), c(3,4), integer(), 1, c(2, 3, 4))
m3 = list(3, c(1,4), 2, 3, c(1, 4))
waldo::compare(m1, m3, x_arg = "m1", y_arg = "m3")
#> `m1[[1]]` is an integer vector ()
#> `m3[[1]]` is a double vector (3)
#>
#> `m1[[2]]`: 3 4
#> `m3[[2]]`: 1 4
#>
#> `m1[[3]]` is an integer vector ()
#> `m3[[3]]` is a double vector (2)
#>
#> `m1[[4]]`: 1
#> `m3[[4]]`: 3
#>
#> `m1[[5]]`: 2 3 4
#> `m3[[5]]`: 1 4
The console output is color coded:
Created on 2022-03-16 by the reprex package (v2.0.1)
Upvotes: 3
Reputation: 8602
Both Darren and EJJ decided to go along and use %*%
If we don't mind the default behaviour of NA in boolean comparison, we can use a simple for-loop
or lapply
lapply(seq(la), function(i)a[[i]] == b[[i]])
If we want some security that the lists are 'matchable' we can compare their outer and inner lengths and wrap this in a function as below
compare_each_list_element <- function(a, b){
la <- length(lla <- lengths(a))
lb <- length(llb <- lengths(b))
if(la != lb || any(lla != llb)){
warning('Either length(a) != length(b) or some length within a is not equal to some length within b!')
return(FALSE)
}
lapply(seq(la), function(i)a[[i]] == b[[i]])
}
compare_each_list_element(list(1:3, 2, c(3, 2)), list(1:3, 3, c(2, NA)))
[[1]]
[1] TRUE TRUE TRUE
[[2]]
[1] FALSE
[[3]]
[1] FALSE NA
Upvotes: 2
Reputation: 1523
You can try using the purrr
library to get your desired output, that examines each element. I used %in%
instead of ==
if NAs
exist. Also this assumes each list has the same structure to work.
library(purrr)
purrr::map2(m1, m3, function(x, y) x %in% y)
Upvotes: 4