Is there an R function that allows me to group a list together based off identical characters

Viewed 57
i <- 1 
while (i <= length(files)) {
  start <- i 
  end <- start + 3
  
  v <- files[start:end]
  y <- fname[start:end]
  date_range <- substr(y[1], 7, 23)
  html_block <- make_div(v, date_range)
  
  top <- readLines("header.html")
  bottom <- readLines("footer.html")
  
  
  # This will write just the div block
  write(x = html_block, file = paste0(date_range, "-block.html"))
  
  # This will write a working website
  write(x = c(top, "<body>", html_block, "</body>", bottom), 
        file = paste0(date_range, "-website.html"))
  
  i <- i + 4
}

So, this link will provide a reference on how I've gotten here. And the above code is in fact doing exactly what I want it to. The goal was for it to loop through the lengthy list of files that I have and then make a div for each file whose only difference is file type (i.e. everything else in the file name was the same). However, that is not what this is doing as this just takes every 4 files and puts them together. The issue with this is that not every 4 files are of the same file (some are just 2 or 3 for example; see fname below for example). So what I was wondering is whether there is a way to cycle through each file name and see whether if the characters from x to y are the same, and if so, those can then be grouped together and then so on.

# fname[1:10]
fname <- c("TOR-D-18580907-18580908.tif",                       
"TOR-D-18580907-18580908.tif-FailToProcess-Data.RDS",
"TOR-D-18580907-18580908.tif-FailToProcess-Plot.png",
"TOR-D-18580907-18580908.tif.png",                   
"TOR-D-18580908-18580909.tif",                       
"TOR-D-18580908-18580909.tif-FailToProcess-Data.RDS",
"TOR-D-18580908-18580909.tif-FailToProcess-Plot.png",
"TOR-D-18580908-18580909.tif.png",                   
"TOR-D-18580910-18580911.tif",                       
"TOR-D-18580910-18580911.tif-FailToProcess-Data.RDS")

The first 10 elements of fname (a variable that contains all of the file names which is significantly more than 10) is displayed above. Is there a way for me to check if the 7th to the 24th element (i.e. 18580907-18580908 which is the date) matches the following one and, if so, can those be grouped together and then continue that cycle for every single file that follows?

3 Answers

You can extract the dates using substr() and then group them in a list, if that is what you want. You then get a list of dates, each containing a vector of filenames:

date = substr(fname,7,23)

directory = list()
for(d in unique(date)){
  directory[[d]]=fname[date==d]
}

The output is:

> directory
$`18580907-18580908`
[1] "TOR-D-18580907-18580908.tif"                       
[2] "TOR-D-18580907-18580908.tif-FailToProcess-Data.RDS"
[3] "TOR-D-18580907-18580908.tif-FailToProcess-Plot.png"
[4] "TOR-D-18580907-18580908.tif.png"                   

$`18580908-18580909`
[1] "TOR-D-18580908-18580909.tif"                       
[2] "TOR-D-18580908-18580909.tif-FailToProcess-Data.RDS"
[3] "TOR-D-18580908-18580909.tif-FailToProcess-Plot.png"
[4] "TOR-D-18580908-18580909.tif.png"                   

$`18580910-18580911`
[1] "TOR-D-18580910-18580911.tif"                       
[2] "TOR-D-18580910-18580911.tif-FailToProcess-Data.RDS"

EDIT: I concede that the answer by r2evans is way better. I was oblivious of the split() function.

Two thoughts:

  1. If you want a list, where each element of it is a vector of the related filenames, then

    groupedlist <- split(fname, substr(fname, 7, 23))
    groupedlist
    # $`18580907-18580908`
    # [1] "TOR-D-18580907-18580908.tif"                        "TOR-D-18580907-18580908.tif-FailToProcess-Data.RDS"
    # [3] "TOR-D-18580907-18580908.tif-FailToProcess-Plot.png" "TOR-D-18580907-18580908.tif.png"                   
    # $`18580908-18580909`
    # [1] "TOR-D-18580908-18580909.tif"                        "TOR-D-18580908-18580909.tif-FailToProcess-Data.RDS"
    # [3] "TOR-D-18580908-18580909.tif-FailToProcess-Plot.png" "TOR-D-18580908-18580909.tif.png"                   
    # $`18580910-18580911`
    # [1] "TOR-D-18580910-18580911.tif"                        "TOR-D-18580910-18580911.tif-FailToProcess-Data.RDS"
    
  2. If you want a vector (perhaps to add to a data.frame identifying which group they belong to, and you don't want to use just substr(.) to group on the substrings, then you can get integer representations of those groups with

    as.integer(factor(substr(fname, 7, 23)))
    #  [1] 1 1 1 1 2 2 2 2 3 3
    

an alternative solution more similar to what you already have would be creating a vector of unique data-ranges, and then extracting the group of files for each data range and then carrying on with the tasks in your loops:

library(stringr)
library(dplyr)
fname <- c("TOR-D-18580907-18580908.tif",                       
           "TOR-D-18580907-18580908.tif-FailToProcess-Data.RDS",
           "TOR-D-18580907-18580908.tif-FailToProcess-Plot.png",
           "TOR-D-18580907-18580908.tif.png",                   
           "TOR-D-18580908-18580909.tif",                       
           "TOR-D-18580908-18580909.tif-FailToProcess-Data.RDS",
           "TOR-D-18580908-18580909.tif-FailToProcess-Plot.png",
           "TOR-D-18580908-18580909.tif.png",                   
           "TOR-D-18580910-18580911.tif",                       
           "TOR-D-18580910-18580911.tif-FailToProcess-Data.RDS")

# create a vector of unique date ranges
(date_range_unique_vec <- str_sub(fname, start = 7, end = 23) %>% 
    unique())

for (each_date_range in date_range_unique_vec) {
  
  # extract group of file names for each unique date range
  group_fnames <- fname[str_detect(fname, each_date_range)]
  
  { # from here!!
    html_block <- make_div(group_fnames, each_date_range)
    top <- readLines("header.html")
    bottom <- readLines("footer.html")
    
    # This will write just the div block
    write(x = html_block, file = paste0(each_date_range, "-block.html"))
    
    # This will write a working website
    write(x = c(top, "<body>", html_block, "</body>", bottom),
          file = paste0(each_date_range, "-website.html"))
    
  }# to here, I cannot run locally.  
  

  # 
  cat(each_date_range, "\n")
  cat(group_fnames, "\n")
  cat("\n")
}

Note: I cannot check the optional { } block in the middle of the loop in my computer. While I think it should be fine, maybe you might have to adapt it a little.

Related