Skip to content

Commit

Permalink
refactor: #1294: Yield Genotype matcher response as enumerable preven…
Browse files Browse the repository at this point in the history
…t memory exceptions.
  • Loading branch information
zabeen committed May 3, 2024
1 parent 00583ce commit b460b48
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ public class MatchCalculationFunctions
MatchPredictionParameters = input.MatchPredictionParameters
});

var response = new GenotypeMatcherResponse
var matcherResponse = new GenotypeMatcherResponse
{
MatchPredictionParameters = input.MatchPredictionParameters,
PatientInfo = BuildSubjectResult(result.PatientResult, frequencySet.PatientSet, input.Patient),
DonorInfo = BuildSubjectResult(result.DonorResult, frequencySet.DonorSet, input.Donor),
MatchedGenotypePairs = result.GenotypeMatchDetails.ToSingleDelimitedString()
MatchedGenotypePairs = result.GenotypeMatchDetails.ToFormattedStrings(),
};

return new JsonResult(response);
return new JsonResult(matcherResponse);
}

private static SubjectResult BuildSubjectResult(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Atlas.MatchPrediction.ExternalInterface.Models.HaplotypeFrequencySet;
using System.Collections.Generic;
using Atlas.MatchPrediction.ExternalInterface.Models.HaplotypeFrequencySet;
using Atlas.MatchPrediction.Models;

namespace Atlas.MatchPrediction.Functions.Models.Debug
Expand All @@ -10,9 +11,9 @@ public class GenotypeMatcherResponse
public SubjectResult DonorInfo { get; set; }

/// <summary>
/// Patient-donor genotype pairs (represented as a single, formatted string) and their match counts.
/// Patient-donor genotype pairs and their match counts.
/// </summary>
public string MatchedGenotypePairs { get; set; }
public IEnumerable<string> MatchedGenotypePairs { get; set; }
}

public class SubjectResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,48 +24,35 @@ public static string ToSingleDelimitedString(this Dictionary<PhenotypeInfo<strin
return builder.ToString();
}

public static string ToSingleDelimitedString(this IEnumerable<GenotypeMatchDetails> genotypeMatchDetails)
/// <summary>
/// Converts a collection of GenotypeMatchDetails to a collection of formatted strings.
/// Uses yield return to avoid creating a large collection in memory.
/// </summary>
public static IEnumerable<string> ToFormattedStrings(this IEnumerable<GenotypeMatchDetails> genotypeMatchDetails)
{
// ReSharper disable once PossibleMultipleEnumeration - `IsNullOrEmpty` extension method does not enumerate full collection
if (genotypeMatchDetails.IsNullOrEmpty())
{
return "No available genotype match details.";
yield return "No available genotype match details.";
}

var builder = new StringBuilder();

var genotypePairs = BuildGenotypePairsWithMatchCounts(genotypeMatchDetails);
yield return $"Total{FieldDelimiter}" +
$"A{FieldDelimiter}" +
$"B{FieldDelimiter}" +
$"C{FieldDelimiter}" +
$"DQB1{FieldDelimiter}" +
$"DRB1{FieldDelimiter}" +
$"{BuildGenotypeLikelihoodHeader("P-")}{FieldDelimiter}" +
$"{BuildGenotypeLikelihoodHeader("D-")}";

foreach (var pair in genotypePairs)
// ReSharper disable once PossibleMultipleEnumeration
foreach (var details in genotypeMatchDetails.OrderByDescending(x => x.MatchCount))
{
builder.AppendLine(pair);
yield return $"{BuildCounts(details.MatchCount, details.MatchCounts)}{FieldDelimiter}" +
$"{details.PatientGenotype.ToDelimitedString(details.PatientGenotypeLikelihood)}{FieldDelimiter}" +
$"{details.DonorGenotype.ToDelimitedString(details.DonorGenotypeLikelihood)}";
}

return builder.ToString();
}

private static IEnumerable<string> BuildGenotypePairsWithMatchCounts(IEnumerable<GenotypeMatchDetails> genotypeMatchDetails)
{
var header = $"Total{FieldDelimiter}" +
$"A{FieldDelimiter}" +
$"B{FieldDelimiter}" +
$"C{FieldDelimiter}" +
$"DQB1{FieldDelimiter}" +
$"DRB1{FieldDelimiter}" +
$"{BuildGenotypeLikelihoodHeader("P-")}{FieldDelimiter}" +
$"{BuildGenotypeLikelihoodHeader("D-")}";

var formattedStrings = new List<string> { header };
formattedStrings.AddRange(
genotypeMatchDetails
.OrderByDescending(x => x.MatchCount)
.Select(x =>
$"{BuildCounts(x.MatchCount, x.MatchCounts)}{FieldDelimiter}" +
$"{x.PatientGenotype.ToDelimitedString(x.PatientGenotypeLikelihood)}{FieldDelimiter}" +
$"{x.DonorGenotype.ToDelimitedString(x.DonorGenotypeLikelihood)}"));

return formattedStrings;

string BuildCounts(int totalCount, LociInfo<int?> locusCounts) =>
$"{totalCount}{FieldDelimiter}" +
$"{locusCounts.A}{FieldDelimiter}" +
Expand Down
2 changes: 1 addition & 1 deletion Atlas.MatchPrediction/Models/GenotypeMatchDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class GenotypeMatchDetails
public decimal DonorGenotypeLikelihood { get; set; }
public LociInfo<int?> MatchCounts { get; set; }
public ISet<Locus> AvailableLoci { get; set; }
public int MatchCount => MatchCounts.Reduce((locus, value, accumulator) => accumulator + value ?? accumulator, 0);
public int MatchCount => MatchCounts.Reduce((_, value, accumulator) => accumulator + value ?? accumulator, 0);
public int MismatchCount => (AvailableLoci.Count * 2) - MatchCount;
}
}

0 comments on commit b460b48

Please sign in to comment.