Skip to content

Commit 33387a1

Browse files
authored
Added task 3642
1 parent 678327b commit 33387a1

File tree

3 files changed

+268
-0
lines changed
  • src
    • main/kotlin/g3601_3700/s3642_find_books_with_polarized_opinions
    • test/kotlin/g3601_3700/s3642_find_books_with_polarized_opinions

3 files changed

+268
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
3642\. Find Books with Polarized Opinions
2+
3+
Easy
4+
5+
Table: `books`
6+
7+
+-------------+---------+
8+
| Column Name | Type |
9+
+-------------+---------+
10+
| book_id | int |
11+
| title | varchar |
12+
| author | varchar |
13+
| genre | varchar |
14+
| pages | int |
15+
+-------------+---------+
16+
book_id is the unique ID for this table. Each row contains information about a book including its genre and page count.
17+
18+
Table: `reading_sessions`
19+
20+
+----------------+---------+
21+
| Column Name | Type |
22+
+----------------+---------+
23+
| session_id | int |
24+
| book_id | int |
25+
| reader_name | varchar |
26+
| pages_read | int |
27+
| session_rating | int |
28+
+----------------+---------+
29+
session_id is the unique ID for this table. Each row represents a reading session where someone read a portion of a book. session_rating is on a scale of 1-5.
30+
31+
Write a solution to find books that have **polarized opinions** - books that receive both very high ratings and very low ratings from different readers.
32+
33+
* A book has polarized opinions if it has `at least one rating ≥ 4` and `at least one rating ≤ 2`
34+
* Only consider books that have **at least** `5` **reading sessions**
35+
* Calculate the **rating spread** as (`highest_rating - lowest_rating`)
36+
* Calculate the **polarization score** as the number of extreme ratings (`ratings ≤ 2 or ≥ 4`) divided by total sessions
37+
* **Only include** books where `polarization score ≥ 0.6` (at least `60%` extreme ratings)
38+
39+
Return _the result table ordered by polarization score in **descending** order, then by title in **descending** order_.
40+
41+
The result format is in the following example.
42+
43+
**Example:**
44+
45+
**Input:**
46+
47+
books table:
48+
49+
+---------+------------------------+---------------+----------+-------+
50+
| book_id | title | author | genre | pages |
51+
+---------+------------------------+---------------+----------+-------+
52+
| 1 | The Great Gatsby | F. Scott | Fiction | 180 |
53+
| 2 | To Kill a Mockingbird | Harper Lee | Fiction | 281 |
54+
| 3 | 1984 | George Orwell | Dystopian| 328 |
55+
| 4 | Pride and Prejudice | Jane Austen | Romance | 432 |
56+
| 5 | The Catcher in the Rye | J.D. Salinger | Fiction | 277 |
57+
+---------+------------------------+---------------+----------+-------+
58+
59+
reading\_sessions table:
60+
61+
+------------+---------+-------------+------------+----------------+
62+
| session_id | book_id | reader_name | pages_read | session_rating |
63+
+------------+---------+-------------+------------+----------------+
64+
| 1 | 1 | Alice | 50 | 5 |
65+
| 2 | 1 | Bob | 60 | 1 |
66+
| 3 | 1 | Carol | 40 | 4 |
67+
| 4 | 1 | David | 30 | 2 |
68+
| 5 | 1 | Emma | 45 | 5 |
69+
| 6 | 2 | Frank | 80 | 4 |
70+
| 7 | 2 | Grace | 70 | 4 |
71+
| 8 | 2 | Henry | 90 | 5 |
72+
| 9 | 2 | Ivy | 60 | 4 |
73+
| 10 | 2 | Jack | 75 | 4 |
74+
| 11 | 3 | Kate | 100 | 2 |
75+
| 12 | 3 | Liam | 120 | 1 |
76+
| 13 | 3 | Mia | 80 | 2 |
77+
| 14 | 3 | Noah | 90 | 1 |
78+
| 15 | 3 | Olivia | 110 | 4 |
79+
| 16 | 3 | Paul | 95 | 5 |
80+
| 17 | 4 | Quinn | 150 | 3 |
81+
| 18 | 4 | Ruby | 140 | 3 |
82+
| 19 | 5 | Sam | 80 | 1 |
83+
| 20 | 5 | Tara | 70 | 2 |
84+
+------------+---------+-------------+------------+----------------+
85+
86+
**Output:**
87+
88+
+---------+------------------+---------------+-----------+-------+---------------+--------------------+
89+
| book_id | title | author | genre | pages | rating_spread | polarization_score |
90+
+---------+------------------+---------------+-----------+-------+---------------+--------------------+
91+
| 1 | The Great Gatsby | F. Scott | Fiction | 180 | 4 | 1.00 |
92+
| 3 | 1984 | George Orwell | Dystopian | 328 | 4 | 1.00 |
93+
+---------+------------------+---------------+-----------+-------+---------------+--------------------+
94+
95+
**Explanation:**
96+
97+
* **The Great Gatsby (book\_id = 1):**
98+
* Has 5 reading sessions (meets minimum requirement)
99+
* Ratings: 5, 1, 4, 2, 5
100+
* Has ratings ≥ 4: 5, 4, 5 (3 sessions)
101+
* Has ratings ≤ 2: 1, 2 (2 sessions)
102+
* Rating spread: 5 - 1 = 4
103+
* Extreme ratings (≤2 or ≥4): All 5 sessions (5, 1, 4, 2, 5)
104+
* Polarization score: 5/5 = 1.00 (≥ 0.6, qualifies)
105+
* **1984 (book\_id = 3):**
106+
* Has 6 reading sessions (meets minimum requirement)
107+
* Ratings: 2, 1, 2, 1, 4, 5
108+
* Has ratings ≥ 4: 4, 5 (2 sessions)
109+
* Has ratings ≤ 2: 2, 1, 2, 1 (4 sessions)
110+
* Rating spread: 5 - 1 = 4
111+
* Extreme ratings (≤2 or ≥4): All 6 sessions (2, 1, 2, 1, 4, 5)
112+
* Polarization score: 6/6 = 1.00 (≥ 0.6, qualifies)
113+
* **Books not included:**
114+
* To Kill a Mockingbird (book\_id = 2): All ratings are 4-5, no low ratings (≤2)
115+
* Pride and Prejudice (book\_id = 4): Only 2 sessions (< 5 minimum)
116+
* The Catcher in the Rye (book\_id = 5): Only 2 sessions (< 5 minimum)
117+
118+
The result table is ordered by polarization score in descending order, then by book title in descending order.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Write your MySQL query statement below
2+
# #Easy #Database #2025_08_10_Time_490_ms_(100.00%)_Space_0.0_MB_(100.00%)
3+
WITH book_stats AS (
4+
SELECT
5+
book_id,
6+
COUNT(*) AS total_sessions,
7+
SUM(CASE WHEN session_rating <> 3 THEN 1 ELSE 0 END) AS extreme_ratings,
8+
MAX(session_rating) AS max_rating,
9+
MIN(session_rating) AS min_rating,
10+
SUM(CASE WHEN session_rating > 3 THEN 1 ELSE 0 END) AS high_ratings,
11+
SUM(CASE WHEN session_rating <= 2 THEN 1 ELSE 0 END) AS low_ratings
12+
FROM reading_sessions
13+
GROUP BY book_id
14+
)
15+
SELECT
16+
bs.book_id,
17+
b.title,
18+
b.author,
19+
b.genre,
20+
b.pages,
21+
(bs.max_rating - bs.min_rating) AS rating_spread,
22+
ROUND(bs.extreme_ratings * 1.0 / bs.total_sessions, 2) AS polarization_score
23+
FROM book_stats bs
24+
JOIN books b USING (book_id)
25+
WHERE
26+
bs.total_sessions >= 5
27+
AND bs.high_ratings > 0
28+
AND bs.low_ratings > 0
29+
AND (bs.extreme_ratings * 1.0 / bs.total_sessions) >= 0.6
30+
ORDER BY polarization_score DESC, b.title DESC;
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package g3601_3700.s3642_find_books_with_polarized_opinions
2+
3+
import org.hamcrest.CoreMatchers.equalTo
4+
import org.hamcrest.MatcherAssert.assertThat
5+
import org.junit.jupiter.api.Test
6+
import org.zapodot.junit.db.annotations.EmbeddedDatabase
7+
import org.zapodot.junit.db.annotations.EmbeddedDatabaseTest
8+
import org.zapodot.junit.db.common.CompatibilityMode
9+
import java.io.BufferedReader
10+
import java.io.FileNotFoundException
11+
import java.io.FileReader
12+
import java.sql.SQLException
13+
import java.util.stream.Collectors
14+
import javax.sql.DataSource
15+
16+
@EmbeddedDatabaseTest(
17+
compatibilityMode = CompatibilityMode.MySQL,
18+
initialSqls = [
19+
(
20+
"CREATE TABLE books (" +
21+
" book_id INT PRIMARY KEY," +
22+
" title VARCHAR(255)," +
23+
" author VARCHAR(255)," +
24+
" genre VARCHAR(50)," +
25+
" pages INT" +
26+
");" +
27+
"INSERT INTO books (book_id, title, author, genre, pages) VALUES" +
28+
"(1, 'The Great Gatsby', 'F. Scott', 'Fiction', 180)," +
29+
"(2, 'To Kill a Mockingbird', 'Harper Lee', 'Fiction', 281)," +
30+
"(3, '1984', 'George Orwell', 'Dystopian', 328)," +
31+
"(4, 'Pride and Prejudice', 'Jane Austen', 'Romance', 432)," +
32+
"(5, 'The Catcher in the Rye', 'J.D. Salinger', 'Fiction', 277);" +
33+
"CREATE TABLE reading_sessions (" +
34+
" session_id INT PRIMARY KEY," +
35+
" book_id INT," +
36+
" reader_name VARCHAR(100)," +
37+
" pages_read INT," +
38+
" session_rating INT," +
39+
" FOREIGN KEY (book_id) REFERENCES books(book_id)" +
40+
");" +
41+
"INSERT INTO reading_sessions (session_id, book_id, " +
42+
"reader_name, pages_read, session_rating) VALUES" +
43+
"(1, 1, 'Alice', 50, 5)," +
44+
"(2, 1, 'Bob', 60, 1)," +
45+
"(3, 1, 'Carol', 40, 4)," +
46+
"(4, 1, 'David', 30, 2)," +
47+
"(5, 1, 'Emma', 45, 5)," +
48+
"(6, 2, 'Frank', 80, 4)," +
49+
"(7, 2, 'Grace', 70, 4)," +
50+
"(8, 2, 'Henry', 90, 5)," +
51+
"(9, 2, 'Ivy', 60, 4)," +
52+
"(10, 2, 'Jack', 75, 4)," +
53+
"(11, 3, 'Kate', 100, 2)," +
54+
"(12, 3, 'Liam', 120, 1)," +
55+
"(13, 3, 'Mia', 80, 2)," +
56+
"(14, 3, 'Noah', 90, 1)," +
57+
"(15, 3, 'Olivia', 110, 4)," +
58+
"(16, 3, 'Paul', 95, 5)," +
59+
"(17, 4, 'Quinn', 150, 3)," +
60+
"(18, 4, 'Ruby', 140, 3)," +
61+
"(19, 5, 'Sam', 80, 1)," +
62+
"(20, 5, 'Tara', 70, 2);"
63+
),
64+
],
65+
)
66+
internal class MysqlTest {
67+
@Test
68+
@Throws(SQLException::class, FileNotFoundException::class)
69+
fun testScript(@EmbeddedDatabase dataSource: DataSource) {
70+
dataSource.connection.use { connection ->
71+
connection.createStatement().use { statement ->
72+
statement.executeQuery(
73+
BufferedReader(
74+
FileReader(
75+
(
76+
"src/main/kotlin/g3601_3700/" +
77+
"s3642_find_books_with_" +
78+
"polarized_opinions/" +
79+
"script.sql"
80+
),
81+
),
82+
)
83+
.lines()
84+
.collect(Collectors.joining("\n"))
85+
.replace("#.*?\\r?\\n".toRegex(), ""),
86+
).use { resultSet ->
87+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(true))
88+
assertThat<String>(resultSet.getNString(1), equalTo<String>("1"))
89+
assertThat<String>(
90+
resultSet.getNString(2),
91+
equalTo<String>("The Great Gatsby"),
92+
)
93+
assertThat<String>(
94+
resultSet.getNString(3),
95+
equalTo<String>("F. Scott"),
96+
)
97+
assertThat<String>(resultSet.getNString(4), equalTo<String>("Fiction"))
98+
assertThat<String>(resultSet.getNString(5), equalTo<String>("180"))
99+
assertThat<String>(resultSet.getNString(6), equalTo<String>("4"))
100+
assertThat<String>(resultSet.getNString(7), equalTo<String>("1.00"))
101+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(true))
102+
assertThat<String>(resultSet.getNString(1), equalTo<String>("3"))
103+
assertThat<String>(resultSet.getNString(2), equalTo<String>("1984"))
104+
assertThat<String>(
105+
resultSet.getNString(3),
106+
equalTo<String>("George Orwell"),
107+
)
108+
assertThat<String>(
109+
resultSet.getNString(4),
110+
equalTo<String>("Dystopian"),
111+
)
112+
assertThat<String>(resultSet.getNString(5), equalTo<String>("328"))
113+
assertThat<String>(resultSet.getNString(6), equalTo<String>("4"))
114+
assertThat<String>(resultSet.getNString(7), equalTo<String>("1.00"))
115+
assertThat<Boolean>(resultSet.next(), equalTo<Boolean>(false))
116+
}
117+
}
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)