My personal strategy is to always read data from a samplesheet which then auto-detects whether a sample is paired-end or single-end and stores this information in a so-called meta map (inspired by nf-core) which then passes this information to the modules.
Example:
Say you have a samplesheet that is a three-column csv file with a header:
$ cat samplesheet.csv
sample,r1,r2
sample1,/Users/atpoint/sample1_R1.fq.gz,/Users/atpoint/sample1_R2.fq.gz
sample2,/Users/atpoint/sample2_R1.fq.gz,
Read this into Nextflow and detect whether r2 has an entry. If so it is paired-end, else it is single-end:
# main.nf
nextflow.enable.dsl = 2
// Channel for the samplesheet
ch_samplesheet = Channel.fromPath("/Users/atpoint/samplesheet.csv")
// Parse it line by line
ch_reads = ch_samplesheet.splitCsv(header:true).map {
// This is the read1 and read2 entry
r1 = it['r1']
r2 = it['r2']
// Detect wiether single-end or paired-end
is_singleEnd = r2.toString()=='' ? true : false
// The "meta" map, which is a Nextflow/Groovy map with id (the sample name) and a single_end logical entry
meta = [id: it['sample'], single_end: is_singleEnd]
// We return a nested map, the first entry is the meta map, the second one is the read(s)
r2.toString()=='' ? [meta, [r1]] : [meta, [r1, r2]]
}
If you .view()
the ch_reads
you get:
[[id:sample1, single_end:false], [/Users/atpoint/sample1_R1.fq.gz, /Users/atpoint/sample1_R2.fq.gz]]
[[id:sample2, single_end:true], [/Users/atpoint/sample2_R1.fq.gz]]
A process that can use that could be:
process A {
tag "$meta.id"
input:
tuple val(meta), path(reads)
script:
// The meta map holds the single/paired information that can now easily be parsed in the module in a standardized way
if(meta.single_end){
"""
do_single_end_things with ${reads}
"""
} else {
"""
do_paired_end_things ${reads}[0] ${reads}[1]
"""
}
}
A(ch_reads)
The meta map idea (cudos nf-core) is simple, yet versatile and powerful as it allows standardized parsing of such metadata-like information (single_end, libtype, id/sample_name, any other value you can think of) and most importantly, it allows to process single-end paired end data at the same time. All the decision making is based on the meta map and therefore automated if the modules are coded appropriately as above.
Hope that helps.