Eswar
Eswar

Reputation: 309

R : Swapping 2 values in a nested list

[[1]]
[[1]][[1]]
[1]   1  46 107  69   1

[[1]][[2]]
[1]   1 146 145  71  92   1
####################
[[2]]
[[2]][[1]]
[1]  1 46 18 92  1

[[2]][[2]]
[1]   1 127 145  53 168   1

Assume I have 2 nested list, as shown above, I'm looking for function where I can update (say 46) in both the list with some other number in list (say 92) and update 92's with 46 without altering the structure of the list

Expected output will be something like this

[[1]]
[[1]][[1]]
[1]   1  92 107  69   1

[[1]][[2]]
[1]   1 146 145  71  46 1
####
[[2]]
[[2]][[1]]
[1]  1 92 18 46 1

[[2]][[2]]
[1]   1 127 145  53 168   1

Rlist library has functions like list.find/list.findi which works only for named nested list. Mine is not a named list

Upvotes: 3

Views: 225

Answers (2)

989
989

Reputation: 12937

This is another way to achieve that. First you simply convert your list to vector (unlist(l)). Do your necessary swaps and convert it back to your list (relist(x, skeleton = l)).

x <- unlist(l)
a <- which(x==46)
b <- which(x==92)
x[a] <- 92
x[b] <- 46
relist(x, skeleton = l)

Benchmarking

library(microbenchmark)
l <- list(list(c(1, 46, 107, 69, 1), c(1, 146, 145, 71, 92, 1)), list(
    c(1, 46, 18, 92, 1), c(1, 127, 145, 53, 168, 1)))

f_m0h3n <- function(l){x <- unlist(l);a <- which(x==46);b <- which(x==92);x[a] <- 92;x[b] <- 46;relist(x, l);}
f_jakub <- function(li) rapply(li, function(x) ifelse(x == 46, 92,ifelse(x==92, 46, x)), how = "list")
all.equal(f_m0h3n(l), f_jakub(l))
# [1] TRUE
microbenchmark(f_m0h3n(l), f_jakub(l))

# Unit: microseconds
       # expr     min      lq     mean   median       uq     max neval
 # f_m0h3n(l) 100.942 103.509 109.7108 107.3580 111.6355 204.879   100
 # f_jakub(l) 126.178 131.738 142.8850 137.9405 143.7150 357.148   100

Larger scale

library(microbenchmark)
set.seed(123)
l <- list(list(sample(1000), sample(2000)),list(sample(1000), sample(2000)))

all.equal(f_m0h3n(l), f_jakub(l))
# [1] TRUE
microbenchmark(f_m0h3n(l), f_jakub(l))

# Unit: microseconds
       # expr      min        lq      mean    median       uq      max neval
 # f_m0h3n(l)  588.973  615.0645  896.9371  651.2065  692.268 2827.242   100
 # f_jakub(l) 1022.683 1053.9070 1914.0769 1253.0115 2848.842 3287.898   100

It is evident that f_m0h3n works better than f_jakub. The difference is even more significant for larger scales (the time is reduced almost by half).

Upvotes: 2

jakub
jakub

Reputation: 5104

Could it be a simple rapply? See this example, where 46 is replaced by 92 (and the other way round, as added by @akrun):

li = list(list(c(1, 46, 107, 69, 1),
               c(1, 146, 145, 71, 92, 1)))
# [[1]]
# [[1]][[1]]
# [1]   1  46 107  69   1
# 
# [[1]][[2]]
# [1]   1 146 145  71  92   1

rapply(li, function(x) ifelse(x == 46, 92,ifelse(x==92, 46, x)), how = "list")

# [[1]]
# [[1]][[1]]
# [1]   1  92 107  69   1
# 
# [[1]][[2]]
# [1]   1 146 145  71  46   1

The how = "list" makes sure you get the original structure back.

Upvotes: 2

Related Questions