From d412cb4994905ab492317c4e320fa3659010b214 Mon Sep 17 00:00:00 2001 From: Swetank Mohanty Date: Fri, 13 Mar 2026 17:21:01 +0400 Subject: [PATCH 1/2] hotfix: removed unused imports --- .../com/shortthirdman/primekit/coderbyte/PatternAnalyzer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/shortthirdman/primekit/coderbyte/PatternAnalyzer.java b/src/main/java/com/shortthirdman/primekit/coderbyte/PatternAnalyzer.java index ccd7f47..61239e0 100644 --- a/src/main/java/com/shortthirdman/primekit/coderbyte/PatternAnalyzer.java +++ b/src/main/java/com/shortthirdman/primekit/coderbyte/PatternAnalyzer.java @@ -6,7 +6,6 @@ import com.shortthirdman.primekit.core.strategy.PremiumTaxCalculation; import com.shortthirdman.primekit.core.strategy.StandardTaxCalculation; import com.shortthirdman.primekit.core.strategy.TaxCalculationStrategy; -import lombok.RequiredArgsConstructor; import java.util.Objects; From ceddba5e1ddda5d350d56bf009c7452bf2bd9445 Mon Sep 17 00:00:00 2001 From: Swetank Mohanty Date: Fri, 13 Mar 2026 17:29:20 +0400 Subject: [PATCH 2/2] hackerrank: Added implementation for spam text classifier --- .../hackerrank/SpamTextClassifier.java | 99 ++++++ .../hackerrank/SpamTextClassifierTest.java | 316 ++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 src/main/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifier.java create mode 100644 src/test/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifierTest.java diff --git a/src/main/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifier.java b/src/main/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifier.java new file mode 100644 index 0000000..4a04454 --- /dev/null +++ b/src/main/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifier.java @@ -0,0 +1,99 @@ +package com.shortthirdman.primekit.hackerrank; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Spam Text Classifier: + * A text is "spam" if it contains ≥ 2 occurrences of words from spamWords (case-insensitive, exact matches). Otherwise, return "not_spam". + * @author ShortThirdMan + * @since 1.1.2 + */ +public class SpamTextClassifier { + + /** + * Classifies a list of texts as "SPAM" or "NOT SPAM" based on the presence of spam words. + * @param texts the array of texts to classify as spam or not spam + * @param spamWords an array of words that are considered spam indicators + * @return a list of strings, where each element is "SPAM" if the corresponding text contains any of the spam words, and "NOT SPAM" otherwise + */ + public List classifyTexts(List texts, List spamWords) { + if (texts == null || spamWords == null) { + throw new NullPointerException("texts and spam words cannot be null"); + } + + if (texts.isEmpty()) { + return List.of(); + } + + Set spamSet = new HashSet<>(); + for (String word : spamWords) { + spamSet.add(word.toLowerCase()); + } + + List result = new ArrayList<>(texts.size()); + + for (String text : texts) { + + int spamCount = 0; + + // Split words by a single space + String[] words = text.split(" "); + + for (String word : words) { + if (spamSet.contains(word.toLowerCase())) { + spamCount++; + + // Early exit optimization + if (spamCount >= 2) { + result.add("spam"); + break; + } + } + } + + if (spamCount < 2) { + result.add("not_spam"); + } + } + + return result; + } + + /** + * Classifies a list of texts as "SPAM" or "NOT SPAM" based on the presence of spam words. + * @param texts the list of texts to classify + * @param spamWords the list of spam words to check against + * @return a list of classifications ("spam" or "not_spam") for each text in the input list, + * processed in parallel for improved performance + */ + public List classifyTextStream(List texts, List spamWords) { + if (texts == null || spamWords == null) { + throw new NullPointerException("texts and spam words cannot be null"); + } + + if (texts.isEmpty()) { + return List.of(); + } + + Set spamSet = spamWords.stream() + .map(String::toLowerCase) + .collect(Collectors.toSet()); + + return texts.parallelStream() + .map(text -> { + long spamCount = Arrays.stream(text.split(" ")) + .map(String::toLowerCase) + .filter(spamSet::contains) + .limit(2) + .count(); + + return spamCount >= 2 ? "spam" : "not_spam"; + }) + .toList(); + } +} diff --git a/src/test/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifierTest.java b/src/test/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifierTest.java new file mode 100644 index 0000000..fc1cdea --- /dev/null +++ b/src/test/java/com/shortthirdman/primekit/hackerrank/SpamTextClassifierTest.java @@ -0,0 +1,316 @@ +package com.shortthirdman.primekit.hackerrank; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class SpamTextClassifierTest { + + SpamTextClassifier classifier; + + @BeforeEach + void setUp() { + classifier = new SpamTextClassifier(); + } + + @Test + void testSpamWhenTwoSpamWordsPresent() { + List texts = List.of("buy cheap now"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testNotSpamWhenOnlyOneSpamWord() { + List texts = List.of("buy now"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testNotSpamWhenNoSpamWordsPresent() { + List texts = List.of("hello world"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testSpamWhenExactlyTwoSpamWords() { + List texts = List.of("buy cheap"); + List spamWords = List.of("buy", "cheap", "offer"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testSpamWhenMoreThanTwoSpamWords() { + List texts = List.of("buy cheap offer now"); + List spamWords = List.of("buy", "cheap", "offer"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + CASE INSENSITIVITY + ------------------------------------------------ */ + + @Test + void testUpperCaseTextSpamWords() { + List texts = List.of("BUY CHEAP"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testMixedCaseText() { + List texts = List.of("Buy Cheap"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testMixedCaseSpamWords() { + List texts = List.of("buy cheap"); + List spamWords = List.of("BUY", "CHEAP"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + MULTIPLE TEXT INPUTS + ------------------------------------------------ */ + + @Test + void testMultipleTextsMixedResults() { + List texts = List.of( + "buy cheap", + "hello world", + "cheap buy offer" + ); + + List spamWords = List.of("buy", "cheap", "offer"); + + List expected = List.of("spam", "not_spam", "spam"); + + assertEquals(expected, + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + DUPLICATE WORDS + ------------------------------------------------ */ + + @Test + void testDuplicateSpamWordInText() { + List texts = List.of("buy buy"); + List spamWords = List.of("buy"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testDuplicateSpamWordsInSpamList() { + List texts = List.of("buy cheap"); + List spamWords = List.of("buy", "buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + EMPTY INPUT TESTS + ------------------------------------------------ */ + + @Test + void testEmptyTextsList() { + List texts = List.of(); + List spamWords = List.of("buy"); + + assertEquals(List.of(), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testEmptySpamWordsList() { + List texts = List.of("buy cheap"); + List spamWords = List.of(); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testEmptyStringText() { + List texts = List.of(""); + List spamWords = List.of("buy"); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + WHITESPACE EDGE CASES + ------------------------------------------------ */ + + @Test + void testMultipleSpacesBetweenWords() { + List texts = List.of("buy cheap"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testLeadingSpaces() { + List texts = List.of(" buy cheap"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testTrailingSpaces() { + List texts = List.of("buy cheap "); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testWhitespaceOnlyText() { + List texts = List.of(" "); + List spamWords = List.of("buy"); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + TOKENIZATION LIMITATIONS + ------------------------------------------------ */ + + @Test + void testTabSeparatedWords() { + List texts = List.of("buy\tcheap"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + @Test + void testCommaSeparatedWords() { + List texts = List.of("buy, cheap"); + List spamWords = List.of("buy", "cheap"); + + assertEquals(List.of("not_spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + UNICODE TEXT + ------------------------------------------------ */ + + @Test + void testUnicodeSpamWords() { + List texts = List.of("купить дешево"); + List spamWords = List.of("купить", "дешево"); + + assertEquals(List.of("spam"), + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + PARALLEL STREAM CONSISTENCY + ------------------------------------------------ */ + + @Test + void testParallelStreamProducesSameResults() { + List texts = List.of( + "buy cheap", + "hello world", + "cheap buy offer" + ); + + List spamWords = List.of("buy", "cheap", "offer"); + + List sequential = + classifier.classifyTexts(texts, spamWords); + + List parallel = + classifier.classifyTextStream(texts, spamWords); + + assertEquals(sequential, parallel); + } + + /* ------------------------------------------------ + EXCEPTION TESTS + ------------------------------------------------ */ + + @Test + void testNullTextsThrowsException() { + List spamWords = List.of("buy"); + + assertThrows(NullPointerException.class, () -> + classifier.classifyTexts(null, spamWords)); + } + + @Test + void testNullSpamWordsThrowsException() { + List texts = List.of("buy cheap"); + + assertThrows(NullPointerException.class, () -> + classifier.classifyTexts(texts, null)); + } + + @Test + void testTextContainingNullThrowsException() { + List texts = Arrays.asList("buy cheap", null); + List spamWords = List.of("buy"); + + assertThrows(NullPointerException.class, () -> + classifier.classifyTexts(texts, spamWords)); + } + + /* ------------------------------------------------ + STREAM VERSION EXCEPTION TESTS + ------------------------------------------------ */ + + @Test + void testParallelMethodNullTextsThrowsException() { + List spamWords = List.of("buy"); + + assertThrows(NullPointerException.class, () -> + classifier.classifyTextStream(null, spamWords)); + } + + @Test + void testParallelMethodNullSpamWordsThrowsException() { + List texts = List.of("buy cheap"); + + assertThrows(NullPointerException.class, () -> + classifier.classifyTextStream(texts, null)); + } +} \ No newline at end of file