2

This relates to my other question, and may help me get to a solution. The following structure is a 3x1 matrix with each element containing a list.

cb <- structure(list(c("apples", "and", "pears"), c("one", "banana"
      ), c("pears", "oranges", "and", "pizza")), .Dim = c(3L, 1L),   
      .Dimnames = list(NULL, "s"))

I'd like to recursively assign 4 as the length of each matrix element, and then concatenate them all, so that the result is

# [1] "apples"  "and"     "pears"   NA        "one"     "banana"   
# [6]  NA        NA       "pears"   "oranges" "and"     "pizza"  

Here's the manual way to do it.

`length<-`(cb[[1]],4)
# [1] "apples" "and"    "pears"  NA      
`length<-`(cb[[2]],4)
# [1] "one"    "banana" NA       NA      
`length<-`(cb[[3]],4)
# [1] "pears"   "oranges" "and"     "pizza"

I'd like to do this without looping, either by using recursion or writing a bquote expression. I think I need to use do.call, but I can't figure out the right way to do it. My code is right now is

do.call(`length<-`, list(c(cb, recursive=TRUE), 12))
# [1] "apples"  "and"     "pears"   "one"     "banana"  "pears"    
# [6] "oranges" "and"     "pizza"   NA        NA        NA   

but this appends the NA values to the end of the concatenated vector, and I want to recursively append them because I probably will not know how many rows (list elements) cb contains. There are recursive = arguments in both unlist and c, maybe those need to be used here.

An answer that replaces x with the current list level, like in the following, would work.

replicate(3, quote(`length<-`(cb[[x]], 4L)))
#[[1]]
#`length<-`(cb[[x]], 4L)  # x = 1
#
#[[2]]
#`length<-`(cb[[x]], 4L)  # x = 2
#
#[[3]]
#`length<-`(cb[[x]], 4L)  # x = 3
Rich Scriven
  • 97,041
  • 11
  • 181
  • 245
  • Somegthing like `lapply(cb, function(x){if(length(x) < 4){append(x, rep(NA, times = 4-length(x)))}else{x}})`? – jdharrison Sep 03 '14 at 20:55
  • Oh, I forgot to mention...no `*apply` functions. I'll add that. – Rich Scriven Sep 03 '14 at 20:56
  • There are `recursive` arguments in `unlist` and `c` that I *know* are there for this purpose. I want to learn how to use them – Rich Scriven Sep 03 '14 at 21:00
  • @jdharrison - I'm leaning toward recursion in `cb[[1:3]]`, but you can't do that. But there are other things like `cb[[1,exact=TRUE]]` and `cb[[1,1,exact=TRUE]]` and `cb[[c(1,2)]]` that might be of interest. – Rich Scriven Sep 03 '14 at 21:04
  • Why recursion? You have one level of nesting here. Is there a better example where recursion required? Where the level of nested elements is known? – MrFlick Sep 03 '14 at 21:06
  • Well I want to go down and across at the same time. I don't know the technical terminology, but I thought it was recursion. – Rich Scriven Sep 03 '14 at 21:08
  • Now that I look further into it, I may need to use an `expression` – Rich Scriven Sep 03 '14 at 21:14
  • What about defining a recursive function? `resize.recursive <- function(cb, len = 4) if (length(cb) == 0) NULL else c("length<-"(cb[[1]], len), Recall(cb[-1, , drop = FALSE], len))` – flodel Sep 03 '14 at 21:22
  • @flodel - that looks like one very good idea, yes. I was unaware of `Recall`. – Rich Scriven Sep 03 '14 at 21:29

2 Answers2

0

Don't know if this is what you are looking for, but I'd give it a try:

    eval(parse(text=paste(sep="","c(",paste(sep="","cb[[",1:length(cb),"]][1:4]",collapse=","),")")))

    [1] "apples"  "and"     "pears"   NA        "one"     "banana"  NA       
    [8] NA        "pears"   "oranges" "and"     "pizza"
nicola
  • 24,005
  • 3
  • 35
  • 56
  • Thanks for the thought, but `eval(parse(text=...))` can have problems and its use is not recommended. – Rich Scriven Sep 03 '14 at 22:32
  • What kind of problems can have? – nicola Sep 04 '14 at 03:50
  • Well a small example would be `x <- paste(letters[1], 5, sep = "\'"); eval(parse(text = x))`. `eval` goes with `call` (and others), and `parse` goes with `deparse`. – Rich Scriven Sep 04 '14 at 03:59
  • Of course it will have problems if you have syntax errors in your text. But I don't think that `eval(parse())` may have unpredictable behaviour. If that were the case, how could you possibly `source` a file? – nicola Sep 04 '14 at 04:06
  • Did you look at `x`? There are no syntax errors there. – Rich Scriven Sep 04 '14 at 04:08
  • Yes, it's `a'5`. What has it supposed to mean? What did you expect the parser to do? – nicola Sep 04 '14 at 04:10
  • The point is that it didn't evaluate when we called `parse` on it. – Rich Scriven Sep 04 '14 at 04:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/60587/discussion-between-nicola-and-richard-scriven). – nicola Sep 04 '14 at 04:13
  • That's a bad example on my part, but it shouldn't be done. I can't think of an example right now but it happened to me the other day. – Rich Scriven Sep 04 '14 at 04:17
  • Can you point to a resource that explain why it shouldn't be done? I'm curious, since I've used it many times without any problem. My guess at this stage is that you didn't use it properly. – nicola Sep 04 '14 at 04:32
  • http://stackoverflow.com/questions/13649979/what-specifically-are-the-dangers-of-evalparse A few of the people answering this question are contributors and write package that we all use – Rich Scriven Sep 04 '14 at 04:38
  • Thank you. The fact is that it is almost always a sub-obtimal (often poor) solution. In this case, for instance, it is certainly worse than lapply(cb,`length<-`,4), but I thought that you didn't want to use *apply and so I proposed this. Anyway, there aren't "hidden dangers" in the use of eval(parse()), at least no more than sourcing a file, if you do things properly. – nicola Sep 04 '14 at 04:48
0

Here are two possible ways, the first by flodel (in the comments) works very well.

resize.recursive <- function(x, len = 4L)
{
    if (!length(x)) NULL
    else c(`length<-`(x[[1]], len), 
           Recall(x[-1, , drop = FALSE], len))
}

resize.recursive(cb)
# [1] "apples"  "and"     "pears"   NA        "one"     "banana"  NA       
# [8] NA        "pears"   "oranges" "and"     "pizza"  

And on this one I bit the bullet on the looping and went with lapply

replaceLength <- function(x, lev, len = 4L) `length<-`(x[[lev]], len)
unlist(lapply(1:3, replaceLength, x = cb))
# [1] "apples"  "and"     "pears"   NA        "one"    "banana"  NA 
# [8] NA        "pears"   "oranges" "and"     "pizza"  

which is the same as

unlist(lapply(cb, `length<-`, 4L))
# [1] "apples"  "and"     "pears"   NA        "one"     "banana"  NA       
# [8] NA        "pears"   "oranges" "and"     "pizza"  

I'm still open for other possibilities!

Rich Scriven
  • 97,041
  • 11
  • 181
  • 245