Home > Blockchain >  Adding a list element for an ID based on a specific number
Adding a list element for an ID based on a specific number

Time:04-18

I have a list of elements that hold values in them. I would to write an if statement where if their isn't a specific number of elements for a specific ID (e.g., A, B, C) then add the appropriate number of elements, and assign an NA as the value in the element. I would like the output to look something like expected_ID. Is there an efficient way of doing this?

library(lubridate)
library(tidyverse)
library(purrr)

date <- rep_len(seq(dmy("01-01-2011"), dmy("31-07-2011"), by = "days"), 200)
ID <- rep(c("A","B", "C"), 200)
df <- data.frame(date = date,
                 x = runif(length(date), min = 60000, max = 80000),
                 y = runif(length(date), min = 800000, max = 900000),
                 ID)

df$Month <- month(df$date)

int1 <- df %>%
  # arrange(ID) %>%   # skipped for readability of result
  mutate(new = floor_date(date, '10 day')) %>%
  mutate(new = if_else(day(new) == 31, new - days(10), new)) %>% 
  group_by(ID, new) %>%
  filter(Month == "1") %>% 
  group_split()

names(int1) <- sapply(int1, function(x) paste(x$ID[1]))

int1 <- int1[-c(6, 8, 9)]


expected_ID <- list(int1[[1]], int1[[2]], int1[[3]], int1[[4]], int1[[5]], NA, int1[[6]], NA, NA)
names(expected_ID) <- c(rep("A", 3), rep("B", 3), rep("C", 3))

CodePudding user response:

It's not usually desirable to create lists with repeated names, and it would be better to store these data in a hierarchical structure. This is necessary to achieve your intended output, but after having done that, we can get the data back to the format you've specified. Comments are in the code block below.

# split the list into a list of nested lists
lst <- split(int1, names(int1))

# fill each inner list to the desired length
# the use of pmax() ensures that rep() will not be sent an invalid negative value
# '3' here is your desired list length
filled_lst <- lapply(lst, \(x) list(x, rep(list(NA), pmax(0, 3 - length(x))))) 

# convert to desired flattened output format
flat_lst <- unlist(unlist(filled_lst, recursive = F), recursive = F)
names(flat_lst) <- sub('(.).*', '\\1', names(flat_lst))

For posterity, here is my original answer, which worked on the example in which ID was a list of vectors.

# split the list into a list of nested lists
lst <- split(ID, names(ID))

str(lst)
List of 3
 $ A:List of 3
  ..$ A: int [1:3] 1 2 3
  ..$ A: int [1:3] 4 5 6
  ..$ A: int [1:3] 7 8 9
 $ B:List of 2
  ..$ B: int [1:2] 1 2
  ..$ B: int [1:2] 3 4
 $ C:List of 1
  ..$ C: int [1:3] 1 2 3

# fill each nested list to the desired length
# the use of pmax() ensures that rep() will not be sent an invalid negative value
# '3' here is your desired list length
filled_lst <- lapply(lst, \(x) c(x, rep(NA, pmax(0, 3 - length(x))))) 

str(filled_lst)
List of 3
 $ A:List of 3
  ..$ A: int [1:3] 1 2 3
  ..$ A: int [1:3] 4 5 6
  ..$ A: int [1:3] 7 8 9
 $ B:List of 3
  ..$ B: int [1:2] 1 2
  ..$ B: int [1:2] 3 4
  ..$  : logi NA
 $ C:List of 3
  ..$ C: int [1:3] 1 2 3
  ..$  : logi NA
  ..$  : logi NA

# convert to desired flattened output format
flat_lst <- unlist(filled_lst, recursive = F)
names(flat_lst) <- gsub('\\d|.\\. ', '', names(flat_lst))

str(flat_lst)
List of 9
 $ A: int [1:3] 1 2 3
 $ A: int [1:3] 4 5 6
 $ A: int [1:3] 7 8 9
 $ B: int [1:2] 1 2
 $ B: int [1:2] 3 4
 $ B: logi NA
 $ C: int [1:3] 1 2 3
 $ C: logi NA
 $ C: logi NA
  • Related