How to easily concatenate nested lists without losing the attributes? - r

I need a base solution to concatenate two lists into a specific nested structure while preserving the attributes.
Using c() gives me the wanted structure, but the attributes are gone.
Map(c, L1, L2)
# [[1]]
# [[1]][[1]]
# [1] 1 2
#
# [[1]][[2]]
# [1] 3 4
#
#
# [[2]]
# [[2]][[1]]
# [1] 1 2
#
# [[2]][[2]]
# [1] 3 4
Using list() saves the attributes, but I want the structure one level less deeply nested.
Map(list, L1, L2)
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [1] 1 2
#
# attr(,"foo")
# [1] 42
#
# [[1]][[2]]
# [[1]][[2]][[1]]
# [1] 3 4
#
# attr(,"foo")
# [1] 42
#
#
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
# [1] 1 2
#
# attr(,"foo")
# [1] 42
#
# [[2]][[2]]
# [[2]][[2]][[1]]
# [1] 3 4
#
# attr(,"foo")
# [1] 42
Finally following code gives me what I want
lapply(1:2, function(i) {
r <- c(L1[[i]], L2[[i]])
r[[1]] <- `attributes<-`(r[[1]], attributes(L1[[i]]))
r[[2]] <- `attributes<-`(r[[2]], attributes(L2[[i]]))
r
})
Is there perhaps a less awkward base R way to do this?
Wanted output
# [[1]]
# [[1]][[1]]
# [1] 1 2
# attr(,"foo")
# [1] 42
#
# [[1]][[2]]
# [1] 3 4
# attr(,"foo")
# [1] 42
#
#
# [[2]]
# [[2]][[1]]
# [1] 1 2
# attr(,"foo")
# [1] 42
#
# [[2]][[2]]
# [1] 3 4
# attr(,"foo")
# [1] 42
Data
L1 <- list(`attr<-`(list(1:2), "foo", 42), `attr<-`(list(1:2), "foo", 42))
L2 <- list(`attr<-`(list(3:4), "foo", 42), `attr<-`(list(3:4), "foo", 42))

Related

purrr - how to apply recursively a function with changing arguments

Ideally I would like to make use of purrr's accumulate function or similar.
Let's say I want to make use of utils::combn function iteratively, and get all the intermediate results (ideally put inside a list of lists).
In example below, initially, parameter x = 4, thus m will be also 4 (but (x, m) could be (5, 5), (6, 6), ...). Then, after first loop, x will be previous result, whilst m goes down by one, iteratively until m = 2.
n1 <- combn(x = 4, m = 4, simplify = FALSE)
n2 <- map(n1, ~ combn(.x, 3, simplify = FALSE))
n3 <- map(n2, ~ map(., ~ combn(.x, 2, simplify = FALSE)))
> n1
[[1]]
[1] 1 2 3 4
> n2
[[1]]
[[1]][[1]]
[1] 1 2 3
[[1]][[2]]
[1] 1 2 4
[[1]][[3]]
[1] 1 3 4
[[1]][[4]]
[1] 2 3 4
> n3
[[1]]
[[1]][[1]]
[[1]][[1]][[1]]
[1] 1 2
[[1]][[1]][[2]]
[1] 1 3
[[1]][[1]][[3]]
[1] 2 3
[[1]][[2]]
[[1]][[2]][[1]]
[1] 1 2
[[1]][[2]][[2]]
[1] 1 4
[[1]][[2]][[3]]
[1] 2 4
[[1]][[3]]
[[1]][[3]][[1]]
[1] 1 3
[[1]][[3]][[2]]
[1] 1 4
[[1]][[3]][[3]]
[1] 3 4
[[1]][[4]]
[[1]][[4]][[1]]
[1] 2 3
[[1]][[4]][[2]]
[1] 2 4
[[1]][[4]][[3]]
[1] 3 4
As you can imagine, I want to get all possible combinations, e.g.:
choose(4, 4) -> choose(result, 3) -> choose(result, 2).
Any help or ideas would be much appreciated.
You can use accumulate + map_depth:
combn_recur <- function(n) {
accumulate(c(n, 0:(n-2)),
~ map_depth(.x, .y, combn, m = n-.y, simplify = FALSE))[-1]
}
all.equal(combn_recur(4), c(n1, n2, n3))
# TRUE
combn_recur(3)
# [[1]]
# [1] 1 2 3
#
# [[2]]
# [[2]][[1]]
# [1] 1 2
#
# [[2]][[2]]
# [1] 1 3
#
# [[2]][[3]]
# [1] 2 3
combn_recur(2)
# [[1]]
# [1] 1 2
combn_recur(1)
# Error in .f(.x[[i]], ...) : n < m

What data type is the json file content in R language?

I have a json file on my computer.
Is my output a dictionary or list or string?
Below is my code in Rstudio:
install.packages("rjson")
library("rjson")
output <- fromJSON(file = "provdata.json")
It depends on the json content.
## vector
rjson::fromJSON('[1,2,3]')
# [1] 1 2 3
## list
rjson::fromJSON('[1,2,3]', simplify = FALSE)
# [[1]]
# [1] 1
# [[2]]
# [1] 2
# [[3]]
# [1] 3
## another list
rjson::fromJSON('[[1,2,3]]')
# [[1]]
# [1] 1 2 3
## dictionary, aka named-list
rjson::fromJSON('{"a":1,"b":2}')
# $a
# [1] 1
# $b
# [1] 2

Get subsets between one element and the previous same element

Consider a vector:
vec <- c(1, 3, 4, 3, 3, 1, 1)
I'd like to get, for each element of the vector, a subset of the values in between the nth element and its previous occurrence.
The expected output is:
f(vec)
# [[1]]
# [1] 1
#
# [[2]]
# [1] 3
#
# [[3]]
# [1] 4
#
# [[4]]
# [1] 3 4 3
#
# [[5]]
# [1] 3 3
#
# [[6]]
# [1] 1 3 4 3 3 1
#
# [[7]]
# [1] 1 1
We may loop over the sequence of the vector, get the index of the last match of the same element ('i1') from the previous elements of the vector and get the sequence (:) to subset the vector
lapply(seq_along(vec), function(i) {
i1 <- tail(which(vec[1:(i-1)] == vec[i]), 1)[1]
i1[is.na(i1)] <- i
vec[i1:i]
})
-output
[[1]]
[1] 1
[[2]]
[1] 3
[[3]]
[1] 4
[[4]]
[1] 3 4 3
[[5]]
[1] 3 3
[[6]]
[1] 1 3 4 3 3 1
[[7]]
[1] 1 1

Use mapply or lapply to nested list

I want to apply a sample function to a nested list (I will call this list bb) and I also have a list of numbers (I will call this list k) to be supplied in the sample function. I would like each of the numbers in k to iterate through all the values of each list in bb. How to do this using mapply or lapply?
Here are the data:
k <- list(1,2,4,3) #this is the list of numbers to be supplied in the `sample.int` function
b1 <- list(c(1,2,3),c(2,3,4),c(3,4,5),c(4,5,6)) #The first list of bb
b2 <- list(c(1,2),c(2,3),c(3,4),c(4,5), c(5,6)) #The second list of bb
bb <- list(b1,b2) #This is list bb containing b1 and b2 whose values are to be iterated through
I created this mapply function but it didn't get the expected outcome:
mapply(function(x, y) {
x[sample.int(y,y, replace = TRUE)]
}, bb,k, SIMPLIFY = FALSE)
This only returns 10 output values but I would like each number of k to loop through all values of the two lists in bb and so there should be 10*2 outputs for the two lists in bb. I might be using mapply in the wrong way and so I would appreciate if anyone can point me to the right direction!
outer is your friend. It's normally used to calculate the outer matrix product. Consider:
outer(1:3, 2:4)
1:3 %o% 2:4 ## or
# [,1] [,2] [,3]
# [1,] 2 3 4
# [2,] 4 6 8
# [3,] 6 9 12
It also has a FUN= argument that defaults to "*". However it enables you to calculate any function over the combinations of x and y cross-wise, i.e. x[1] X y[1], x[1] X y[2], ... whereas *apply functions only calculate x[1] X y[1], x[2] X y[2], .... So let's do it:
FUN <- Vectorize(function(x, y) x[sample.int(y, y)])
set.seed(42)
res <- outer(bb, k, FUN)
res
# [,1] [,2] [,3] [,4]
# [1,] List,1 List,2 List,4 List,3
# [2,] List,1 List,2 List,4 List,3
This result looks a little weird, but we may easily unlist it.
res <- unlist(res, recursive=F)
Result
res
# [[1]]
# [1] 1 2 3
#
# [[2]]
# [1] 1 2
#
# [[3]]
# [1] 1 2 3
#
# [[4]]
# [1] 2 3 4
#
# [[5]]
# [1] 2 3
#
# [[6]]
# [1] 1 2
#
# [[7]]
# [1] 2 3 4
#
# [[8]]
# [1] 4 5 6
#
# [[9]]
# [1] 1 2 3
#
# [[10]]
# [1] 3 4 5
#
# [[11]]
# [1] 3 4
#
# [[12]]
# [1] 4 5
#
# [[13]]
# [1] 2 3
#
# [[14]]
# [1] 1 2
#
# [[15]]
# [1] 1 2 3
#
# [[16]]
# [1] 2 3 4
#
# [[17]]
# [1] 3 4 5
#
# [[18]]
# [1] 2 3
#
# [[19]]
# [1] 3 4
#
# [[20]]
# [1] 1 2
VoilĂ , 20 results.

List all combinations of strings that together cover all given elements

Say I am given the following strings:
1:{a,b,c,t}
2:{b,c,d}
3:{a,c,d}
4:{a,t}
I want to make a program that will give me all different combinations of these strings, where each combination has to include each given letter.
So for example the above combinations are strings {1&2, 1&3, 2&3&4, 1&2&3&4, 2&4}.
I was thinking of doing this with for loops, where the program would look at the first string, find which elements are missing, then work down through the list to find strings which have these letters. However I think this idea will only find combinations of two strings, and also it requires listing all letters to the program which seems very un-economical.
I think something like this should work.
sets <- list(c('a', 'b', 'c', 't'),
c('b', 'c', 'd'),
c('a', 'c', 'd'),
c('a', 't'))
combinations <- lapply(2:length(sets),
function(x) combn(1:length(sets), x, simplify=FALSE))
combinations <- unlist(combinations, FALSE)
combinations
# [[1]]
# [1] 1 2
#
# [[2]]
# [1] 1 3
#
# [[3]]
# [1] 1 4
#
# [[4]]
# [1] 2 3
#
# [[5]]
# [1] 2 4
#
# [[6]]
# [1] 3 4
#
# [[7]]
# [1] 1 2 3
#
# [[8]]
# [1] 1 2 4
#
# [[9]]
# [1] 1 3 4
#
# [[10]]
# [1] 2 3 4
#
# [[11]]
# [1] 1 2 3 4
u <- unique(unlist(sets))
u
# [1] "a" "b" "c" "t" "d"
Filter(function(x) length(setdiff(u, unlist(sets[x]))) == 0, combinations)
# [[1]]
# [1] 1 2
#
# [[2]]
# [1] 1 3
#
# [[3]]
# [1] 2 4
#
# [[4]]
# [1] 1 2 3
#
# [[5]]
# [1] 1 2 4
#
# [[6]]
# [1] 1 3 4
#
# [[7]]
# [1] 2 3 4
#
# [[8]]
# [1] 1 2 3 4
As a start...
I'll edit this answer when I have time. The following result is dependent on the order of choice. I haven't figured out how to flatten the list yet. If I could flatten it, I would sort each result then remove duplicates.
v = list(c("a","b","c","t"),c("b","c","d"),c("a","c","d"),c("a","t"))
allChars <- Reduce(union, v) # [1] "a" "b" "c" "t" "d"
charInList <- function(ch, li) which(sapply(li, function(vect) ch %in% vect))
locations <- sapply(allChars, function(ch) charInList(ch, v) )
# > locations
# $a
# [1] 1 3 4
#
# $b
# [1] 1 2
#
# $c
# [1] 1 2 3
#
# $t
# [1] 1 4
#
# $d
# [1] 2 3
findStillNeeded<-function(chosen){
haveChars <- Reduce(union, v[chosen])
stillNeed <- allChars[!allChars %in% haveChars]
if(length(stillNeed) == 0 ) return(chosen) #terminate if you dont need any more characters
return ( lapply(1:length(stillNeed), function(i) { #for each of the characters you still need
loc <- locations[[stillNeed[i]]] #find where the character is located
lapply(loc, function(j){
findStillNeeded(c(chosen, j)) #when you add this location to the choices, terminate if you dont need any more characters
})
}) )
}
result<-lapply(1:length(v), function(i){
findStillNeeded(i)
})

Resources