Michael
Michael

Reputation: 1381

Filter list objects based on attribute in R

I want to construct an object list and extract objects based on one of their attributes. For example

persons_list <- list(PersonA = list(color = 'red', group = 1),
                     PersonsB = list(color = 'blue', group = 1),
                     PersonsC = list(color = 'green', group = 2))

Now I want to extract all colors as a vector where group is equal to 1 such that the result is:

c('red', 'blue')

I'm not sure if a list is the way to go here, but I chose it because I also want to be able to use the autocomplete/browse function of a list by typing

persons_list$PersonA

Upvotes: 1

Views: 4652

Answers (5)

Morten Grum
Morten Grum

Reputation: 1011

I know this is old but I missed the more general answer.

I would use:

unlist(lapply(persons_list[which(sapply(persons_list, function(person) { person$group==1 }))], function(x) x$color), use.name = FALSE)

Or for easier reading, I'd probably split it into two lines:

First filter:

persons_in_group_1 = persons_list[which(sapply(persons_list, function(person) { person$group==1 }))]

then map the desired property:

unlist(lapply(persons_in_group_1, function(x) x$color ), use.names = FALSE)

Upvotes: 0

DJJ
DJJ

Reputation: 2539

Akrun is always faster. :-)

More generally. You can build some general function to do this job without too much cognitive effort. I haven't build any tool to select things from list yet. But I can drop things with

remove_if <- function(lst,test_fn) {
    ## DD . char or list
    if (class(lst)=="character"){
        unlist(lapply(lst ,function(x){if(!(test_fn(x))) x }))
    } else {

       remove_if_null(lapply(lst ,function(x){if(!(test_fn(x))) x }))}
    }

It removes group 2 then select the part you need.

unlist(lapply(remove_if(persons_list, function(x) x$group==2),"[[",1))

## PersonA PersonsB 
##   "red"   "blue" 

Upvotes: 2

Nate
Nate

Reputation: 10671

library(rlist) has bunch of nice list focused functions to bring some of the tidy verbs to list operations.

library(rlist)
library(magrittr)

list.filter(persons_list, group == 1) %>%
    list.select(color) %>%
    unlist(use.names = F)

Upvotes: 1

Roland
Roland

Reputation: 132706

No, that data structure is suboptimal for your use-case. Use a data.frame:

persons <- data.frame(person = c("A", "B", "C"),
                      color = c("red", "blue", "green"),
                      group = c(1, 1, 2),
                      stringsAsFactors = FALSE)
persons[persons$group == 1, "color"]
#[1] "red"  "blue"

Not only is a tabular structure more natural, lookup is also more efficient.

Upvotes: 1

akrun
akrun

Reputation: 887118

We can do

unlist(lapply(persons_list, function(x) x$color[x$group==1]), use.names = FALSE)
#[1] "red"  "blue"

Upvotes: 2

Related Questions