Home > Enterprise >  Grouping list by object except one field
Grouping list by object except one field

Time:06-30

I have to group list items if the items fields are the same. Above in my code, it works what I want if every id is the same. But product ids are unique. For that reason, I cant make the right grouping.

Here I wrote an example product model but original product model has 20 fields and some of them are object list. Product model is came from api.

I want to make grouping by item except for id.

    data class Product(
    val id: Int,
    val name: String,
    val price: Int,
    val quantity: Int,
    val subModels: List<SubModel>,
    val values: List<String>
)

data class SubModel(
    val name: String,
    val value: String
)

data class GroupedProduct(
    val product: Product,
    val count: Int,
    val totalQuantity: Int,
    val isGroup: Boolean
)

var products = listOf(
    Product(1, "p1", 10, 2, listOf(SubModel("a", "1")), listOf("a")),
    Product(1, "p1", 10, 2, listOf(SubModel("a", "1")), listOf("a")),
    Product(1, "p1", 10, 2, listOf(SubModel("b", "2")), listOf("a")),
    Product(1, "p2", 12, 1, listOf(SubModel("a", "1")), listOf("a")),
    Product(1, "p3", 12, 1, listOf(SubModel("a", "1")), listOf("a")),
)

var groupedPersons: MutableList<GroupedProduct> = mutableListOf()

products.groupBy { it }.forEach {
    if (it.value.size > 1)
        groupedPersons.add(GroupedProduct(it.key, it.value.size, it.value.getTotalQuantity(), true))
    else
        groupedPersons.add(GroupedProduct(it.key, it.value.size, it.value.size, false))
}

fun List<Product>.getTotalQuantity(): Int {
    var totalQuantity = 0
    forEach {
        totalQuantity  = it.quantity
    }
    return totalQuantity
}

groupedPersons.forEach {
        println(it)
    }

Prints:

GroupedProduct(product=Product(id=1, name=p1, price=10, quantity=2, subModels=[SubModel(name=a, value=1)], values=[a]), count=2, totalQuantity=4, isGroup=true)
GroupedProduct(product=Product(id=1, name=p1, price=10, quantity=2, subModels=[SubModel(name=b, value=2)], values=[a]), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=1, name=p2, price=12, quantity=1, subModels=[SubModel(name=a, value=1)], values=[a]), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=1, name=p3, price=12, quantity=1, subModels=[SubModel(name=a, value=1)], values=[a]), count=1, totalQuantity=1, isGroup=false)

When item ids are different grouping results be like that obviously.

GroupedProduct(product=Product(id=1, name=p1, price=10, quantity=2, subModels=[SubModel(name=a, value=1)], values=[a]), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=2, name=p1, price=10, quantity=2, subModels=[SubModel(name=a, value=1)], values=[a]), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=3, name=p1, price=10, quantity=2, subModels=[SubModel(name=b, value=2)], values=[a]), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=4, name=p2, price=12, quantity=1, subModels=[SubModel(name=a, value=1)], values=[a]), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=5, name=p3, price=12, quantity=1, subModels=[SubModel(name=a, value=1)], values=[a]), count=1, totalQuantity=1, isGroup=false)

CodePudding user response:

Change the grouping to

products.groupBy { Triple(it.name, it.price, it.quantity) }.forEach {
    if (it.value.size > 1)
        groupedPersons.add(GroupedProduct(it.value.first(), it.value.size, it.value.getTotalQuantity(), true))
    else
        groupedPersons.add(GroupedProduct(it.value.first(), it.value.size, it.value.size, false))
}

CodePudding user response:

You must replace the id with a meaningless id (like -1 in my example):

data class Product(
  val id: Int,
  val name: String,
  val price: Int,
  val quantity: Int
)

data class GroupedProduct(
  val product: Product,
  val count: Int,
  val totalQuantity: Int,
  val isGroup: Boolean
)

var products = listOf(
  Product(1, "p1", 10, 2),
  Product(2, "p1", 10, 2),
  Product(3, "p1", 10, 2),
  Product(4, "p2", 12, 1),
  Product(5, "p3", 12, 1),
)

val result = products
  .groupBy { it.copy(id = -1) }
  .map { (_, values) ->
    val product = values.first().copy(id = -1, quantity = values.sumOf { it.quantity })  
    GroupedProduct(product, values.count(), values.sumOf { it.quantity }, values.count() > 1)
  }

result.forEach(::println)

Output:

GroupedProduct(product=Product(id=-1, name=p1, price=10, quantity=6), count=3, totalQuantity=6, isGroup=true)
GroupedProduct(product=Product(id=-1, name=p2, price=12, quantity=1), count=1, totalQuantity=1, isGroup=false)
GroupedProduct(product=Product(id=-1, name=p3, price=12, quantity=1), count=1, totalQuantity=1, isGroup=false)
  • Related