0

I'm using the testthat framework, but this question should fit in a larger background. Basically, I have a function that outputs this

list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"))

However, I don't care about the order, so this is also completely fine

list("PL+", c("PW-", "PL+"), "PL+", "PW-", "PW-")

How do I test for equality with expect_equal? I was considering unlist followed by a sort, but that would break the nested structure.

nalzok
  • 11,870
  • 17
  • 57
  • 105

3 Answers3

1

You can use purrr::map_depth to sort the nested lists, then check the lists with setdiff. If the length when you call setdiff is 0, the lists contain the same elements. The tricky thing is that setdiff will yield different results based on the order of its arguments, so you end up calling it twice, once from each direction. This is similar to the double checking done in testthat::expect_setequal.

For simplicity, I'm wrapping this in a function. You could instead do the same thing with a combination of all and %in%, but this seemed for compact.

a1 <- list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"))
b1 <- list("PL+", c("PW-", "PL+"), "PL+", "PW-", "PW-")

nested_equal <- function(l1, l2) {
  left_diff <- setdiff(
    purrr::map_depth(l1, 1, sort),
    purrr::map_depth(l2, 1, sort)
  )
  right_diff <- setdiff(
    purrr::map_depth(l2, 1, sort),
    purrr::map_depth(l1, 1, sort)
  )
  (length(left_diff) == 0) & (length(right_diff) == 0)
}

nested_equal(a1, b1)
#> [1] TRUE

A few test cases with different elements:

a2 <- list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"), "A")
b2 <- list("PL+", c("PW-", "PL+"), "PL+", "PW-", "PW-")

nested_equal(a2, b2)
#> [1] FALSE

a3 <- list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"))
b3 <- list("PL+", c("PW-", "PL+", "X"), "PL+", "PW-", "PW-", "B")

nested_equal(a3, b3)
#> [1] FALSE

To fit in with testthat tests, you could test against a true or false value returned by nested_equal in expect_equal, expect_true, expect_false.

camille
  • 13,812
  • 10
  • 29
  • 45
0

sort() and unlist() would likely be the best bet.

identical(sort(unlist(a1)), sort(unlist(b1)))

You expressed hesitation about breaking the nested structure but note the sort(unlist(...)) isn't permanent. You can still access either list without a problem.

Also, while I'm unfamiliar with the testthat package, expect_equal or expect_identical should provide similar results although if they are equal, the statement executes silently.

> identical(sort(unlist(a1)), sort(unlist(b1)))
[1] TRUE
> a1
[[1]]
[1] "PL+"

[[2]]
[1] "PW-"

[[3]]
[1] "PL+"

[[4]]
[1] "PW-"

[[5]]
[1] "PL+" "PW-"

Data h/t to @camille for the data.

a1 <- list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"))
b1 <- list("PL+", c("PW-", "PL+"), "PL+", "PW-", "PW-")
Cole
  • 9,745
  • 1
  • 7
  • 21
0

Loop through the elements of the lists and check against one another

l1 = list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"))
l2 = list("PL+", c("PW-", "PL+"), "PL+", "PW-", "PW-")
temp = sapply(l1, function(x) sapply(l2, function(y) identical(sort(x), sort(y))))
all(rowSums(temp) > 0) & all(colSums(temp) > 0)
#[1] TRUE
d.b
  • 29,772
  • 5
  • 24
  • 63