Tôi có hai danh sách
first = list(a = 1, b = 2, c = 3)
second = list(a = 2, b = 3, c = 4)
Tôi muốn hợp nhất hai danh sách này để sản phẩm cuối cùng là
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Có một chức năng đơn giản để làm điều này?
Tôi có hai danh sách
first = list(a = 1, b = 2, c = 3)
second = list(a = 2, b = 3, c = 4)
Tôi muốn hợp nhất hai danh sách này để sản phẩm cuối cùng là
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Có một chức năng đơn giản để làm điều này?
Câu trả lời:
Nếu các danh sách luôn có cấu trúc giống nhau, như trong ví dụ, thì một giải pháp đơn giản hơn là
mapply(c, first, second, SIMPLIFY=FALSE)
Map(c, first, second)
, nếu có ai quan tâm.
Map()
có chứamapply()
Đây là một sự chuyển thể rất đơn giản của hàm modList của Sarkar. Bởi vì nó là đệ quy, nó sẽ xử lý các tình huống phức tạp hơn mapply
sẽ xử lý các tình huống tên không khớp bằng cách bỏ qua các mục trong 'thứ hai' không có trong 'thứ nhất'.
appendList <- function (x, val)
{
stopifnot(is.list(x), is.list(val))
xnames <- names(x)
for (v in names(val)) {
x[[v]] <- if (v %in% xnames && is.list(x[[v]]) && is.list(val[[v]]))
appendList(x[[v]], val[[v]])
else c(x[[v]], val[[v]])
}
x
}
> appendList(first,second)
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Đây là hai tùy chọn, tùy chọn đầu tiên:
both <- list(first, second)
n <- unique(unlist(lapply(both, names)))
names(n) <- n
lapply(n, function(ni) unlist(lapply(both, `[[`, ni)))
và thứ hai, chỉ hoạt động nếu chúng có cùng cấu trúc:
apply(cbind(first, second),1,function(x) unname(unlist(x)))
Cả hai đều cho kết quả mong muốn.
apply
đơn giản hóa nó nếu nó có thể. Nó hoạt động nếu nó không thể đơn giản hóa, chẳng hạn như nếu first$c <- c(4,5)
chẳng hạn.
Đây là một số mã mà tôi đã viết xong, dựa trên câu trả lời của @ Andrei nhưng không có tính đơn giản / cao cấp. Ưu điểm là nó cho phép hợp nhất đệ quy phức tạp hơn và cũng có sự khác biệt giữa các phần tử cần được kết nối rbind
và những phần tử chỉ được kết nối với c
:
# Decided to move this outside the mapply, not sure this is
# that important for speed but I imagine redefining the function
# might be somewhat time-consuming
mergeLists_internal <- function(o_element, n_element){
if (is.list(n_element)){
# Fill in non-existant element with NA elements
if (length(n_element) != length(o_element)){
n_unique <- names(n_element)[! names(n_element) %in% names(o_element)]
if (length(n_unique) > 0){
for (n in n_unique){
if (is.matrix(n_element[[n]])){
o_element[[n]] <- matrix(NA,
nrow=nrow(n_element[[n]]),
ncol=ncol(n_element[[n]]))
}else{
o_element[[n]] <- rep(NA,
times=length(n_element[[n]]))
}
}
}
o_unique <- names(o_element)[! names(o_element) %in% names(n_element)]
if (length(o_unique) > 0){
for (n in o_unique){
if (is.matrix(n_element[[n]])){
n_element[[n]] <- matrix(NA,
nrow=nrow(o_element[[n]]),
ncol=ncol(o_element[[n]]))
}else{
n_element[[n]] <- rep(NA,
times=length(o_element[[n]]))
}
}
}
}
# Now merge the two lists
return(mergeLists(o_element,
n_element))
}
if(length(n_element)>1){
new_cols <- ifelse(is.matrix(n_element), ncol(n_element), length(n_element))
old_cols <- ifelse(is.matrix(o_element), ncol(o_element), length(o_element))
if (new_cols != old_cols)
stop("Your length doesn't match on the elements,",
" new element (", new_cols , ") !=",
" old element (", old_cols , ")")
}
return(rbind(o_element,
n_element,
deparse.level=0))
return(c(o_element,
n_element))
}
mergeLists <- function(old, new){
if (is.null(old))
return (new)
m <- mapply(mergeLists_internal, old, new, SIMPLIFY=FALSE)
return(m)
}
Đây là ví dụ của tôi:
v1 <- list("a"=c(1,2), b="test 1", sublist=list(one=20:21, two=21:22))
v2 <- list("a"=c(3,4), b="test 2", sublist=list(one=10:11, two=11:12, three=1:2))
mergeLists(v1, v2)
Kết quả này trong:
$a
[,1] [,2]
[1,] 1 2
[2,] 3 4
$b
[1] "test 1" "test 2"
$sublist
$sublist$one
[,1] [,2]
[1,] 20 21
[2,] 10 11
$sublist$two
[,1] [,2]
[1,] 21 22
[2,] 11 12
$sublist$three
[,1] [,2]
[1,] NA NA
[2,] 1 2
Vâng, tôi biết - có lẽ không phải là hợp nhất hợp lý nhất nhưng tôi có một vòng lặp song song phức tạp mà tôi phải tạo ra một .combine
hàm tùy chỉnh hơn , và do đó tôi đã viết con quái vật này :-)
Nói chung, người ta có thể,
merge_list <- function(...) by(v<-unlist(c(...)),names(v),base::c)
Lưu ý rằng by()
giải pháp trả về một attribute
danh sách d, vì vậy nó sẽ in khác, nhưng vẫn sẽ là một danh sách. Nhưng bạn có thể loại bỏ các thuộc tính với attr(x,"_attribute.name_")<-NULL
. Bạn cũng có thể sử dụng aggregate()
.