<commit_before>//
//  ArticleSorter.swift
//  NetNewsWire
//
//  Created by <NAME> on 9/8/19.
//  Copyright © 2019 Ranchero Software. All rights reserved.
//

import Articles
import Foundation

protocol SortableArticle {
	var sortableName: String { get }
	var sortableDate: Date { get }
	var sortableArticleID: String { get }
	var sortableFeedID: String { get }
}

struct ArticleSorter {
		
	static func sortedByDate<T: SortableArticle>(articles: [T],
												 sortDirection: ComparisonResult,
												 groupByFeed: Bool) -> [T] {
		if groupByFeed {
			return sortedByFeedName(articles: articles, sortByDateDirection: sortDirection)
		} else {
			return sortedByDate(articles: articles, sortDirection: sortDirection)
		}
	}
	
	// MARK: -
		
	private static func sortedByFeedName<T: SortableArticle>(articles: [T],
															 sortByDateDirection: ComparisonResult) -> [T] {
		// Group articles by feed - feed ID is used to differentiate between
		// two feeds that have the same name
		var groupedArticles = Dictionary(grouping: articles) { "\($0.sortableName.lowercased())-\($0.sortableFeedID)" }
		
		// Sort the articles within each group
		for tuple in groupedArticles {
			groupedArticles[tuple.key] = sortedByDate(articles: tuple.value,
													  sortDirection: sortByDateDirection)
		}
		
		// Flatten the articles dictionary back into an array sorted by feed name
		var sortedArticles: [T] = []
		for feedName in groupedArticles.keys.sorted() {
			sortedArticles.append(contentsOf: groupedArticles[feedName] ?? [])
		}
		
		return sortedArticles
	}
	
	private static func sortedByDate<T: SortableArticle>(articles: [T],
														 sortDirection: ComparisonResult) -> [T] {
		let articles = articles.sorted { (article1, article2) -> Bool in
			if article1.sortableDate == article2.sortableDate {
				return article1.sortableArticleID < article2.sortableArticleID
			}
			if sortDirection == .orderedDescending {
				return article1.sortableDate > article2.sortableDate
			}
			
			return article1.sortableDate < article2.sortableDate
		}
		return articles
	}
	
}<commit_msg>Re-wrote sorting code to be smaller and easier to understand<commit_after>//
//  ArticleSorter.swift
//  NetNewsWire
//
//  Created by <NAME> on 9/8/19.
//  Copyright © 2019 Ranchero Software. All rights reserved.
//

import Articles
import Foundation

protocol SortableArticle {
	var sortableName: String { get }
	var sortableDate: Date { get }
	var sortableArticleID: String { get }
	var sortableFeedID: String { get }
}

struct ArticleSorter {
		
	static func sortedByDate<T: SortableArticle>(articles: [T],
												 sortDirection: ComparisonResult,
												 groupByFeed: Bool) -> [T] {
		if groupByFeed {
			return sortedByFeedName(articles: articles, sortByDateDirection: sortDirection)
		} else {
			return sortedByDate(articles: articles, sortDirection: sortDirection)
		}
	}
	
	// MARK: -
		
	private static func sortedByFeedName<T: SortableArticle>(articles: [T],
															 sortByDateDirection: ComparisonResult) -> [T] {
		// Group articles by "feed-feedID" - feed ID is used to differentiate between
		// two feeds that have the same name
		let groupedArticles = Dictionary(grouping: articles) { "\($0.sortableName.lowercased())-\($0.sortableFeedID)" }
		return groupedArticles
			.sorted { $0.key < $1.key }
			.flatMap { (tuple) -> [T] in
				let (_, articles) = tuple
				
				return sortedByDate(articles: articles, sortDirection: sortByDateDirection)
		}
	}
	
	private static func sortedByDate<T: SortableArticle>(articles: [T],
														 sortDirection: ComparisonResult) -> [T] {
		return articles.sorted { (article1, article2) -> Bool in
			if article1.sortableDate == article2.sortableDate {
				return article1.sortableArticleID < article2.sortableArticleID
			}
			if sortDirection == .orderedDescending {
				return article1.sortableDate > article2.sortableDate
			}
			
			return article1.sortableDate < article2.sortableDate
		}
	}
	
}
{'fraction_non_alphanumeric': 0.10025125628140703, 'fraction_numerical': 0.010552763819095477, 'mean_word_length': 3.44804469273743, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3085704', 'n_tokens_mistral': 1466, 'n_tokens_neox': 1170, 'n_words': 319}