Welcome to today’s lesson! We’ll explore the fascinating world of using Kotlin for managing product reviews and putting data aggregation into practice. We’ll begin with a straightforward Starter Task to establish our foundation, then gradually progress to a more advanced solution involving data aggregation. Let’s get started!
For our starter task, we will lay the foundation by implementing basic operations for managing product reviews. These are the methods we will need to implement:
-
addReview(productId: String, reviewId: String, reviewText: String, rating: Int): Boolean
— adds a review to the product specified byproductId
. If a review withreviewId
already exists, it updates the existing review. Returnstrue
if the review was added or updated successfully,false
otherwise. -
getReview(productId: String, reviewId: String): Map<String, Any>?
— returns the review details (reviewText
,rating
, andflagged
fields) for the review specified byreviewId
under the givenproductId
. If the review or product does not exist, returnsnull
. -
deleteReview(productId: String, reviewId: String): Boolean
— deletes the review specified byreviewId
under the givenproductId
. Returnstrue
if the review was deleted,false
otherwise.
Let's look at the code that implements these functionalities:
Kotlin1class ReviewManager { 2 private val products = HashMap<String, MutableMap<String, Map<String, Any>>>() 3 4 fun addReview(productId: String, reviewId: String, reviewText: String, rating: Int): Boolean { 5 if (rating !in 1..5) return false // Invalid rating 6 val productReviews = products.getOrPut(productId) { mutableMapOf() } 7 productReviews[reviewId] = mapOf("text" to reviewText, "rating" to rating, "flagged" to false) 8 return true 9 } 10 11 fun getReview(productId: String, reviewId: String): Map<String, Any>? { 12 return products[productId]?.get(reviewId) 13 } 14 15 fun deleteReview(productId: String, reviewId: String): Boolean { 16 val productReviews = products[productId] 17 if (productReviews != null && productReviews.containsKey(reviewId)) { 18 productReviews.remove(reviewId) 19 if (productReviews.isEmpty()) { 20 products.remove(productId) // Remove product if no reviews left 21 } 22 return true 23 } 24 return false 25 } 26} 27 28 29fun main() { 30 // Instantiate the ReviewManager 31 val reviewManager = ReviewManager() 32 33 // Adding some reviews 34 reviewManager.addReview("p1", "r1", "Great product!", 5) 35 reviewManager.addReview("p1", "r2", "Not bad", 3) 36 37 // Testing getReview method 38 println(reviewManager.getReview("p1", "r1")) // Expected: {text=Great product!, rating=5, flagged=false} 39 println(reviewManager.getReview("p1", "r3")) // Expected: null 40 41 // Testing deleteReview method 42 println(reviewManager.deleteReview("p1", "r2")) // Expected: true 43 println(reviewManager.getReview("p1", "r2")) // Expected: null 44}
This code establishes the foundational methods needed for managing product reviews within a ReviewManager
class. The addReview
method allows for adding a new review or updating an existing one, ensuring each review contains valid rating values between 1 and 5. It utilizes the getOrPut
method to retrieve or initialize a map of reviews for a given product, ensuring smooth addition or updates without requiring separate checks for initialization. The getReview
method retrieves the review details for a specific product, including the review text and rating, returning null
if the product or review doesn't exist. The deleteReview
method facilitates the removal of a specific review, and if no reviews are left for a product, the product itself is removed from the product list. Together, these methods form the basic operations required to manage a collection of product reviews efficiently.
Now, let's extend this with new features.
With our basic review management system in place, we will now introduce new methods to handle more complex operations, such as flagging inappropriate reviews and aggregating review data for a specific product.
Here are the new methods we will add:
-
flagReview(productId: String, reviewId: String): Boolean
— This method flags a specific review as inappropriate for a given product. Returnstrue
if the review was successfully flagged,false
otherwise. -
aggregateReviews(productId: String): Map<String, Any>?
— This method aggregates review data for a given product, providing statistics such as the total number of reviews, the number of flagged reviews, average rating, and review texts excluding flagged ones. If the product does not have any reviews or does not exist, returnsnull
.
First, let's add functionality to flag a review:
Kotlin1class ReviewManager { 2 // Existing methods remain unchanged... 3 4 fun flagReview(productId: String, reviewId: String): Boolean { 5 val productReviews = products[productId] 6 if (productReviews != null && productReviews.containsKey(reviewId)) { 7 val review = productReviews[reviewId]?.toMutableMap() ?: return false 8 review["flagged"] = true 9 productReviews[reviewId] = review 10 return true 11 } 12 return false 13 } 14} 15 16 17fun main() { 18 // Instantiate the ReviewManager 19 val reviewManager = ReviewManager() 20 21 // Adding some reviews 22 reviewManager.addReview("p1", "r1", "Great product!", 5) 23 reviewManager.addReview("p1", "r2", "Not bad", 3) 24 reviewManager.addReview("p1", "r3", "Terrible", 1) 25 26 // Flagging a review 27 reviewManager.flagReview("p1", "r3") 28 29 // Testing flagReview method 30 println(reviewManager.getReview("p1", "r3")) // Expected: {text=Terrible, rating=1, flagged=true} 31}
In this step, we are adding the flagReview
method to our ReviewManager
class. This method enables users to mark a specific review as inappropriate. It checks whether the product and review exist in the dataset, and if they do, it creates a mutable copy of the review using toMutableMap()
with null-safety operators (?.
and ?:
), then sets the flagged
attribute to true
. The use of a mutable copy is necessary because our original review map is immutable, and creating a copy allows us to modify the review's properties safely. If at any point the product or review doesn't exist, or if the conversion to a mutable map fails, the method returns false
. This flagging mechanism is crucial for maintaining the quality and appropriateness of the reviews in the system.
Next, we will implement the method to aggregate reviews:
Kotlin1class ReviewManager { 2 // Existing methods remain unchanged... 3 4 fun aggregateReviews(productId: String): Map<String, Any>? { 5 val productReviews = products[productId] ?: return null // No reviews or product doesn't exist 6 7 val totalReviews = productReviews.size 8 var flaggedReviews = 0 9 var totalRating = 0 10 val reviewTexts = mutableListOf<String>() 11 12 for (review in productReviews.values) { 13 if (review["flagged"] == true) { 14 flaggedReviews++ 15 } else { 16 totalRating += review["rating"] as Int 17 reviewTexts.add(review["text"] as String) 18 } 19 } 20 21 val averageRating = if (totalReviews == flaggedReviews) 0.0 else totalRating.toDouble() / (totalReviews - flaggedReviews) 22 23 return mapOf( 24 "total_reviews" to totalReviews, 25 "flagged_reviews" to flaggedReviews, 26 "average_rating" to averageRating, 27 "review_texts" to reviewTexts 28 ) 29 } 30} 31 32 33fun main() { 34 // Instantiate the ReviewManager 35 val reviewManager = ReviewManager() 36 37 // Adding some reviews 38 reviewManager.addReview("p1", "r1", "Great product!", 5) 39 reviewManager.addReview("p1", "r2", "Not bad", 3) 40 reviewManager.addReview("p1", "r3", "Terrible", 1) 41 42 // Flagging a review 43 reviewManager.flagReview("p1", "r3") 44 45 // Testing the aggregation method 46 println(reviewManager.aggregateReviews("p1")) 47 // Output: 48 // {total_reviews=3, flagged_reviews=1, average_rating=4.0, review_texts=[Great product!, Not bad]} 49}
In this step, the aggregateReviews
method is added to the ReviewManager
class. This method aggregates review data for a given product by calculating various statistics, such as the total number of reviews, the number of flagged reviews, the average rating excluding flagged reviews, and a list of review texts that are not flagged. The method first ensures the product exists and contains reviews. It then iterates through the reviews to collect the necessary data, considering only non-flagged reviews for the average rating and review texts. If all reviews are flagged, the average rating defaults to zero. This aggregation provides a comprehensive overview of a product's review status, useful for both users and administrators.
Great job! Today, you have learned how to manage product reviews and apply data aggregation in practice. We started with basic operations for adding, retrieving, and deleting reviews. Then, we extended our functionality to include flagging reviews and aggregating review data. This gradual build-up demonstrates how to enhance features incrementally and handle more complex data aggregation tasks.
Feel free to practice solving similar challenges to further strengthen your skills. Keep coding, and see you in the next lesson!