DNA sequencing data can be stored in many different formats. In this Kata, we will be looking at SAM formatting. It is a plain text file where every line (excluding the first header lines) contains data about a "read" from whatever sample the file comes from. Rather than focusing on the whole read, we will take two pieces of information: the cigar string and the nucleotide sequence.

The cigar string is composed of numbers and flags. It represents how the read aligns to what is known as a reference genome. A reference genome is an accepted standard for mapping the DNA. 

The nucleotide sequence shows us what bases actually make up that section of DNA. They can be represented with the letters A, T, C, or G.

Example Read: ('36M', 'ACTCTTCTTGCGAAAGTTCGGTTAGTAAAGGGGATG')

The M in the above cigar string stands for "match", and the 36 stands for the length of the nucleotide sequence. Since all 36 bases are given the 'M' distinction, we know they all matched the reference.

Example Read: ('20M10S',  'ACTCTTCTTGCGAAAGTTCGGTTAGTAAAG')

In the above cigar string, only 20 have the "M" distinction, but the length of the actual string of nucleotides is 30. Therefore we know that read did not match the reference. (Don't worry about what the other letters mean. That will be covered in a later kata.)

Your job for this kata is to create a function that determines whether a cigar string fully matches the reference and accounts for all bases. If it does fully match, return True. If the numbers in the string do not match the full length of the string, return 'Invalid cigar'. If it does not fully match, return False.

*Note for C++: Return True, False, or Invalid cigar as strings*

Difficulty: introductory

Starter code:

def is_matched(read):
	

Solutions:
import re

def is_matched(read):
    total = sum([int(num) for num in re.findall(r'\d+', read[0])])
    
    if read[0] == str(len(read[1])) + 'M':
        return True
    elif len(read[1]) != total:
        return 'Invalid cigar'
    else:
        return False


import re

def is_matched(read):
    cigar, seq = read
    matches = re.findall(r'(\d+)([A-Z])', cigar)
    if sum(int(n) for n, _ in matches) != len(seq):
        return 'Invalid cigar'
    return all(sym == 'M' for _, sym in matches)

import re
def is_matched(read):
    if f'{len(read[1])}M' == read[0]:
        return True
    elif sum(int(n) for n in re.findall('\d+', read[0])) != len(read[1]):
        return 'Invalid cigar'
    
    return False

import re
from functools import reduce

def is_matched(read):
    parse = re.findall(r'[0-9]+[A-Z]', read[0])
    cigar = {}
    for i in parse:
        if cigar.get(i[-1]) == None:
            cigar[i[-1]] = 0
        cigar[i[-1]] += int(i[:-1])
    if cigar.get('M') == len(read[1]):
        return True
    elif reduce(lambda x,y: x+y, [v for v in cigar.values()], 0) == len(read[1]):
        return False
    else:
        return 'Invalid cigar'

import re

def is_matched(cig):
    ci = re.split('\D', cig[0])[:-1]
    return 'Invalid cigar' if sum(map(int, ci)) != len(cig[1]) else cig[0] == ci[0]+'M'

import re
def is_matched(read):
    cigar = [int(i) for i in list(filter(None, re.split("[A-Z]",read[0])))]
    if(sum(cigar)==len(read[1])):
        return (len(cigar)==1 and re.split("[0-9]+",read[0])[1]=="M")
    else:
        return 'Invalid cigar'

import re
def is_matched(read):
    num = re.compile(r'\d+')
    a = list(map(int,num.findall(read[0])))
    Dna_length = sum(a[0:len(a)])
    if Dna_length != len(read[1]):      
        return "Invalid cigar"
    else:
        return a[0] == len(read[1]) and str(a[0])+"M" == read[0]

import re
def is_matched(read):
    res = [int(i) for i in re.findall(r"\d+",read[0])]
    if sum(res) != len(read[1]):
        return "Invalid cigar"
    if len(res) == 1 and "M" in read[0]:
        return True
    return False

import re

def is_matched(read):
    nums = [int(v) for v in re.findall(r'\d+', read[0])]
    return 'Invalid cigar' if sum(nums)!=len(read[1]) else read[0]==f'{len(read[1])}M'