N08
N08

Reputation: 1315

Looping over nested list in Rcpp

I have the following nested list that I want to loop over using Rcpp:

nested_list <- list("a"=list("a_a","a_b","a_c"), 
                    "b"=list("b_a", "b_b", "b_c"))

Here is my attempt at it:

#include <Rcpp.h>
#include <iostream>
using namespace Rcpp;


// [[Rcpp::export]]
CharacterVector names_get( Rcpp::List y ){
  return y.names() ;
}

// [[Rcpp::export]]
void output( Rcpp::List y ){

  for(const auto &i : names_get(y))
  {
    Rcpp::List tmp = y[std::string(i)];

    for(const auto &j : integer_names_get(tmp))
      std::cout << j << "\n";
}

It compiles fine, but when I run this as output(nested_list) I get the error Error in output(nested_list) : not compatible with STRSXP

What does this error mean and where/why does it appear?

Upvotes: 2

Views: 1421

Answers (2)

Benjamin Christoffersen
Benjamin Christoffersen

Reputation: 4841

I have the following nested list that I want to loop over using Rcpp

Just to add a more elegant solution, you can use these function to either print the names of the nested lists or the elements of the nested lists:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void print_char_ele(List x) {
  for(auto sub_list : x)
    for(auto ele : List(sub_list)) 
      for(auto ele_i : CharacterVector(ele))
        Rcout << ele_i << '\n';
}

// [[Rcpp::export]]
void print_names(List x) {
  for(auto sub_list : x)
    for(auto nam : CharacterVector(List(sub_list).names())) 
      Rcout << nam << '\n';
}

Here is the result of using the two function

nested_list <- list("a" = list("a_a" = "x" , "a_b" = "y" , "a_c" = "z" ), 
                    "b" = list("b_a" = "xx", "b_b" = "yy", "b_c" = "zz"))

print_char_ele(nested_list)
#R> x
#R> y
#R> z
#R> xx
#R> yy
#R> zz
print_names(nested_list)
#R> a_a
#R> a_b
#R> a_c
#R> b_a
#R> b_b
#R> b_c

What does this error mean and where/why does it appear?

As duckmayr mentions, the list you provided do not have any names and you use a function called integer_names_get which you do not show.

Upvotes: 0

duckmayr
duckmayr

Reputation: 16930

The error

I believe the error is occurring because you are trying to print the names of the sublist when your sublist does not have names:

names(nested_list)
names(nested_list[[1]])
str(nested_list)

> names(nested_list)
[1] "a" "b"
> names(nested_list[[1]])
NULL
> str(nested_list)
List of 2
 $ a:List of 3
  ..$ : chr "a_a"
  ..$ : chr "a_b"
  ..$ : chr "a_c"
 $ b:List of 3
  ..$ : chr "b_a"
  ..$ : chr "b_b"
  ..$ : chr "b_c"

What you have is a list of two lists, each of which has three unnamed elements that are each a character vector of length one.

Rcout

An unrelated but nevertheless important issue is that when using Rcpp, you should really use Rcout rather than cout; from the Rcpp Gallery:

The Writing R Extensions manual, which provides the gold standard of documentation as far as extending R goes, strongly suggests to use Rprintf and REprintf for output (from C/C++ code). The key reason is that these are matched to the usual output and error streams maintained by R itself.

In fact, use of std::cout and std::cerr (as common in standard C++ code) is flagged when running R CMD check and no longer permitted when uploading to CRAN.

Thanks to an initial patch by Jelmer Ypma, which has since been reworked and extended, we have devices Rcout (for standard output) and Rcerr (for standard error) which intercept output and redirect it to R.

Revised code

If you really are working with an object like nested_list and need to get the elements such as "a_a", you can try the following (maybe not the most elegant solution, but gets the job done):

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
void output(List x) {
    int n = x.size();
    for ( int i = 0; i < n; ++i ) {
        List sublist = x[i];
        int m = sublist.size();
        for ( int j = 0; j < m; ++j ) {
            CharacterVector sublist_element = sublist[j];
            Rcout << sublist_element << "\n";
        }
    }
}

Which, when called from R on your example object gives the following:

> output(nested_list)
"a_a"
"a_b"
"a_c"
"b_a"
"b_b"
"b_c"

However, if you really are working with a nested list whose sublists have names, like the following:

# Make a nested list with sub-element names:
nested_list2 <- list("a"=list("a_a"=1,"a_b"=2,"a_c"=3), 
                     "b"=list("b_a"=1, "b_b"=2, "b_c"=3))

Your function gave the following output for me (no errors):

a_a
a_b
a_c
b_a
b_b
b_c

but only after I fixed a typo where you call integer_names_get instead of names_get and left out the ending curly bracket.

Upvotes: 5

Related Questions