From 75e1e071cf94cce8426a517c8ec632539bd6e539 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Mon, 18 Mar 2024 16:52:19 +0100 Subject: [PATCH] Implemented the TopWordsUseCase use case in the Filter library. --- .../Use Cases/FilterWordsUseCase.swift | 5 - .../Sources/Use Cases/TopWordsUseCase.swift | 58 +++++++++ .../Use Cases/TopWordsUseCaseTests.swift | 110 ++++++++++++++++++ 3 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 Libraries/Filter/Kit/Sources/Use Cases/TopWordsUseCase.swift create mode 100644 Libraries/Filter/Test/Tests/Use Cases/TopWordsUseCaseTests.swift diff --git a/Libraries/Filter/Kit/Sources/Use Cases/FilterWordsUseCase.swift b/Libraries/Filter/Kit/Sources/Use Cases/FilterWordsUseCase.swift index 8f45e40..e66f8f3 100644 --- a/Libraries/Filter/Kit/Sources/Use Cases/FilterWordsUseCase.swift +++ b/Libraries/Filter/Kit/Sources/Use Cases/FilterWordsUseCase.swift @@ -69,11 +69,6 @@ public struct FilterWordsUseCase { ) } - print("WORD TERMS: \(terms)") - print("WORD ALL: \(wordsAll)") - print("WORD COUNT: \(wordsCount)") - print("WORD UNIQUE: \(wordsUnique)") - return wordsUnique.sorted { guard $0.count != $1.count else { return $0.word.token < $1.word.token diff --git a/Libraries/Filter/Kit/Sources/Use Cases/TopWordsUseCase.swift b/Libraries/Filter/Kit/Sources/Use Cases/TopWordsUseCase.swift new file mode 100644 index 0000000..0282130 --- /dev/null +++ b/Libraries/Filter/Kit/Sources/Use Cases/TopWordsUseCase.swift @@ -0,0 +1,58 @@ +// +// TopWordsUseCase.swift +// ReviewsParserKit +// +// Created by Javier Cicchelli on 18/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import Foundation + +public struct TopWordsUseCase { + + // MARK: Type aliases + public typealias Input = [[WordCount]] + public typealias Output = [WordCount] + + // MARK: Initialisers + public init() {} + + // MARK: Functions + public func callAsFunction(_ input: Input) -> Output { + let wordCounts = input + .reduce([WordCount](), +) + .reduce(into: [WordCount]()) { partialResult, wordCount in + guard + let wordCountFound = partialResult.first(where: { $0.word == wordCount.word }) + else { + partialResult.append(wordCount) + return + } + + partialResult.removeAll { $0 == wordCountFound } + partialResult.append(.init( + word: wordCount.word, + count: wordCount.count + wordCountFound.count + )) + } + .sorted { + guard $0.count != $1.count else { + return $0.word.token < $1.word.token + } + + return $0.count > $1.count + } + + return wordCounts.count >= .Max.words + ? Array(wordCounts[0...2]) + : wordCounts + } + +} + +// MARK: - Int+Constants +private extension Int { + enum Max { + static let words: Int = 3 + } +} diff --git a/Libraries/Filter/Test/Tests/Use Cases/TopWordsUseCaseTests.swift b/Libraries/Filter/Test/Tests/Use Cases/TopWordsUseCaseTests.swift new file mode 100644 index 0000000..58c4dbf --- /dev/null +++ b/Libraries/Filter/Test/Tests/Use Cases/TopWordsUseCaseTests.swift @@ -0,0 +1,110 @@ +// +// TopWordsUseCaseTests.swift +// ReviewsFilterTest +// +// Created by Javier Cicchelli on 18/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import ReviewsFilterKit +import XCTest + +final class TopWordsUseCaseTests: XCTestCase { + + // MARK: Properties + private var input: TopWordsUseCase.Input! + private var output: TopWordsUseCase.Output! + private var sut: TopWordsUseCase! + + // MARK: Setup + override func setUp() async throws { + sut = .init() + } + + // MARK: Functions + func testCallAsFunction_withInput_hasDifferentWords() { + // GIVEN + input = [[ + .init(word: .init(term: "One", token: "one"), count: 3) + ], [ + .init(word: .init(term: "Two", token: "two"), count: 1) + ], [ + .init(word: .init(term: "Three", token: "three"), count: 5), + .init(word: .init(term: "Four", token: "four"), count: 2), + ]] + + // WHEN + output = sut(input) + + // THEN + XCTAssertFalse(output.isEmpty) + XCTAssertEqual(output.count, 3) + XCTAssertEqual(output, [ + .init(word: .init(term: "Three", token: "three"), count: 5), + .init(word: .init(term: "One", token: "one"), count: 3), + .init(word: .init(term: "Four", token: "four"), count: 2) + ]) + } + + func testCallAsFunction_withInput_hasSomeCommonWords() { + // GIVEN + input = [[ + .init(word: .init(term: "One", token: "one"), count: 3), + .init(word: .init(term: "Two", token: "two"), count: 7), + .init(word: .init(term: "Three", token: "three"), count: 1), + ], [ + .init(word: .init(term: "Four", token: "four"), count: 5), + .init(word: .init(term: "Two", token: "two"), count: 2), + .init(word: .init(term: "Five", token: "five"), count: 6), + ], [ + .init(word: .init(term: "Six", token: "six"), count: 9), + .init(word: .init(term: "Four", token: "four"), count: 4), + .init(word: .init(term: "Two", token: "two"), count: 1), + ]] + + // WHEN + output = sut(input) + + // THEN + XCTAssertFalse(output.isEmpty) + XCTAssertEqual(output.count, 3) + XCTAssertEqual(output, [ + .init(word: .init(term: "Two", token: "two"), count: 10), + .init(word: .init(term: "Four", token: "four"), count: 9), + .init(word: .init(term: "Six", token: "six"), count: 9) + ]) + } + + func testCallAsFunction_withInput_hasFewWords() { + // GIVEN + input = [[ + .init(word: .init(term: "One", token: "one"), count: 3), + ], [ + .init(word: .init(term: "Four", token: "four"), count: 5), + ]] + + // WHEN + output = sut(input) + + // THEN + XCTAssertFalse(output.isEmpty) + XCTAssertEqual(output.count, 2) + XCTAssertEqual(output, [ + .init(word: .init(term: "Four", token: "four"), count: 5), + .init(word: .init(term: "One", token: "one"), count: 3) + ]) + } + + func testCallAsFunction_withEmptyInput() { + // GIVEN + input = [] + + // WHEN + output = sut(input) + + // THEN + XCTAssertTrue(output.isEmpty) + XCTAssertEqual(output, []) + } + +}