如何测试列表是否相等而不考虑顺序?

How to test for list equality regardless of order?

提问人:nalzok 提问时间:7/10/2019 更新时间:7/10/2019 访问量:762

问:

我正在使用 testthat 框架,但这个问题应该适合更大的背景。基本上,我有一个输出这个的函数

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

但是,我不在乎订单,所以这也完全没问题

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

如何测试是否相等?我正在考虑后跟一个 ,但这会破坏嵌套结构。expect_equalunlistsort

r 单元 testing 相等性 测试that

评论


答:

1赞 camille 7/10/2019 #1

您可以使用对嵌套列表进行排序,然后使用 .如果调用时的长度为 0,则列表包含相同的元素。棘手的是,它会根据其参数的顺序产生不同的结果,因此您最终会调用它两次,每个方向一次。这类似于 中执行的双重检查。purrr::map_depthsetdiffsetdiffsetdifftestthat::expect_setequal

为简单起见,我将其包装在一个函数中。相反,您可以使用 和 的组合来执行相同的操作,但这似乎是紧凑的。all%in%

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

一些具有不同元素的测试用例:

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

为了适应测试,您可以针对 、 、 返回的 true 或 false 值进行测试。testthatnested_equalexpect_equalexpect_trueexpect_false

2赞 Cole 7/10/2019 #2

sort()并且可能是最好的选择。unlist()

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

你对打破嵌套结构表示犹豫,但请注意它不是永久性的。您仍然可以毫无问题地访问任一列表。sort(unlist(...))

此外,虽然我不熟悉该包,或者应该提供类似的结果,但如果它们相等,则该语句会静默执行。testthatexpect_equalexpect_identical

> 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-"

数据 h/t 到@camille数据。

a1 <- list("PL+", "PW-", "PL+", "PW-", c("PL+", "PW-"))
b1 <- list("PL+", c("PW-", "PL+"), "PL+", "PW-", "PW-")
0赞 d.b 7/10/2019 #3

遍历列表的元素并相互检查

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