The new matching results.
Merged results with preserved user progress.
export function mergeMatchResults(newResults: MatchResult[]): MatchResult[] {
try {
// Get existing results
const existingResults = getSavedMatchResults();
if (
!existingResults ||
!Array.isArray(existingResults) ||
existingResults.length === 0
) {
console.log("No existing match results to merge, using new results");
return newResults;
}
console.log(
`Merging ${newResults.length} new results with ${existingResults.length} existing results`,
);
// Create a map of existing results for quick lookup by both ID and title
const existingById = new Map<string, MatchResult>();
const existingByTitle = new Map<string, MatchResult>();
existingResults.forEach((match) => {
if (match.kenmeiManga?.id) {
existingById.set(match.kenmeiManga.id.toString(), match);
}
if (match.kenmeiManga?.title) {
existingByTitle.set(match.kenmeiManga.title.toLowerCase(), match);
}
});
// Process new results, preserving user progress from existing matches
const processedResults = newResults.map((newMatch) => {
// Try to find existing match by ID first
let existingMatch = newMatch.kenmeiManga?.id
? existingById.get(newMatch.kenmeiManga.id.toString())
: undefined;
// If not found by ID, try title (case insensitive)
if (!existingMatch && newMatch.kenmeiManga?.title) {
existingMatch = existingByTitle.get(
newMatch.kenmeiManga.title.toLowerCase(),
);
}
// If we found a match AND it has user progress (not pending), preserve it
if (existingMatch && existingMatch.status !== "pending") {
console.log(
`Preserving existing ${existingMatch.status} match for "${newMatch.kenmeiManga?.title}"`,
);
// Take new anilist matches but keep user's selected match and status
return {
...newMatch,
status: existingMatch.status,
selectedMatch: existingMatch.selectedMatch,
matchDate: existingMatch.matchDate,
};
}
// Otherwise use the new match
return newMatch;
});
// Create sets to track what we've processed
const processedIds = new Set<string>();
const processedTitles = new Set<string>();
// Add all processed results to the tracking sets
processedResults.forEach((result) => {
if (result.kenmeiManga?.id) {
processedIds.add(result.kenmeiManga.id.toString());
}
if (result.kenmeiManga?.title) {
processedTitles.add(result.kenmeiManga.title.toLowerCase());
}
});
// Find existing results that weren't in the new results and add them
const unprocessedExistingResults = existingResults.filter(
(existingMatch) => {
// Skip if we already processed this manga by ID
if (
existingMatch.kenmeiManga?.id &&
processedIds.has(existingMatch.kenmeiManga.id.toString())
) {
return false;
}
// Skip if we already processed this manga by title
if (
existingMatch.kenmeiManga?.title &&
processedTitles.has(existingMatch.kenmeiManga.title.toLowerCase())
) {
return false;
}
// This is an existing result that wasn't in the new batch, so include it
return true;
},
);
if (unprocessedExistingResults.length > 0) {
console.log(
`Adding ${unprocessedExistingResults.length} existing results that weren't in the new batch`,
);
}
// Combine processed results with unprocessed existing results
const mergedResults = [...processedResults, ...unprocessedExistingResults];
console.log(`Merged results: ${mergedResults.length} total items`);
// Check how many preserved matches we have
const preservedCount = mergedResults.filter(
(m) => m.status !== "pending",
).length;
console.log(
`Preserved ${preservedCount} user reviews from previous imports`,
);
return mergedResults;
} catch (error) {
console.error("Error merging match results", error);
return newResults; // Fall back to new results on error
}
}
Merges new match results with existing ones to preserve user progress.