Home > Mobile >  Get objects from a list that have matching values for specific attributes and group them: Java 11
Get objects from a list that have matching values for specific attributes and group them: Java 11

Time:08-18

I have a class called Product. From this class I have created 8 objects, and I have added those objects to a list called productList. Each object represents a product that has a name, color and size attribute, and a storage bin that it belongs to. Each product also has a quantityCounted value. I need to go through this list, get all products that have matching attribute values for the following:

productName, attributeSetValue, storageBin

I will need to add each matching set of objects to a group, and then add these groups to a list. (reason: I will need to get each group in the list and calculate the most common quantityCounted figure for that group which will then be the final quantity for that product item.)

For example, from the example code below, I would need:

  • p1, p2, p5 and p6 to all be in one group because all 3 of the above mentioned attribute values match.
  • p3 would be in it's own group because the value of storageBin is different.
  • p4 and p7 would form a group.
  • p8 would be in it's own group because the productName value does not match any of the others.

What is the best way to go about this please? Fairly new to Java - any advice would be appreciated. Thanks in advance.

// Class Declaration 
public class Product
{
    // Instance Variables
    public String id;    
    public String productName;
    public String attributeSetValue;
    public String storageBin;
    public String countedBy;
    public int quantityCounted;
   
    // Constructor Declaration of Class here...

    // Creating objects using new operator:
    Product p1 = new Product("1", "gumboots", "Blue_7", "Bin 1", "John", 12);
    Product p2 = new Product("2", "gumboots", "Blue_7", "Bin 1", "Sally", 13);   
    Product p3 = new Product("3", "gumboots", "Red_8", "Bin 2", "John", 15);
    Product p4 = new Product("4", "gumboots", "Red_8", "Bin 1", "Sally", 15);
    Product p5 = new Product("5", "gumboots", "Blue_7", "Bin 1", "Sue", 13);
    Product p6 = new Product("6", "gumboots", "Blue_7", "Bin 1", "Bob", 13);
    Product p7 = new Product("7", "gumboots", "Red_8", "Bin 1", "Sue", 16);
    Product p8 = new Product("8", "sandals", "Red_10", "Bin 1", "Bob", 10);
    
    //Add product objects to list:
    List<Product> productList = Arrays.asList(p1,p2,p3,p4,p5,p6,p7,p8);     
    
}

CodePudding user response:

You can use the streams API to group the Product objects according to your criteria.

Note: Explanations after the code.

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Product {
    private String id;    
    private String productName;
    private String attributeSetValue;
    private String storageBin;
    private String countedBy;
    private int quantityCounted;

    public Product(String id,
                   String productName,
                   String attributeSetValue,
                   String storageBin,
                   String countedBy,
                   int quantityCounted) {
        this.id = id;
        this.productName = productName;
        this.attributeSetValue = attributeSetValue;
        this.storageBin = storageBin;
        this.countedBy = countedBy;
        this.quantityCounted = quantityCounted;
    }

    private static void display(List<Product> list) {
        System.out.println();
        if (list != null) {
            if (list.size() > 0) {
                list.forEach(System.out::println);
            }
            else {
                System.out.println("Empty list.");
            }
        }
        else {
            System.out.println("Null list.");
        }
    }

    public String getId() {
        return id;
    }

    public String getProductName() {
        return productName;
    }

    public String getAttributeSetValue() {
        return attributeSetValue;
    }

    public String getStorageBin() {
        return storageBin;
    }

    public String getCountedBy() {
        return countedBy;
    }

    public int getQuantityCounted() {
        return quantityCounted;
    }

    public String toString() {
        return String.format("%s %s %s %s %s %d",
                             id,
                             productName,
                             attributeSetValue,
                             storageBin,
                             countedBy,
                             quantityCounted);
    }

    public static void main(String[] args) {
        Product p1 = new Product("1", "gumboots", "Blue_7", "Bin 1", "John", 12);
        Product p2 = new Product("2", "gumboots", "Blue_7", "Bin 1", "Sally", 13);   
        Product p3 = new Product("3", "gumboots", "Red_8", "Bin 2", "John", 15);
        Product p4 = new Product("4", "gumboots", "Red_8", "Bin 1", "Sally", 15);
        Product p5 = new Product("5", "gumboots", "Blue_7", "Bin 1", "Sue", 13);
        Product p6 = new Product("6", "gumboots", "Blue_7", "Bin 1", "Bob", 13);
        Product p7 = new Product("7", "gumboots", "Red_8", "Bin 1", "Sue", 16);
        Product p8 = new Product("8", "sandals", "Red_10", "Bin 1", "Bob", 10);
        
        //Add product objects to list:
        List<Product> productList = Arrays.asList(p1,p2,p3,p4,p5,p6,p7,p8);
        Map<String, List<Product>> map = productList.stream()
                                                    .collect(Collectors.groupingBy(p -> p.getProductName()   p.getAttributeSetValue()   p.getStorageBin()));
        map.values()
           .forEach(Product::display);
    }
}

The groupingBy method concatenates the productName, attributeSetValue and storageBin for each Product object in the list. Those Product objects that have identical concatenations are added to a List and that List serves as the value for a Map where the concatenation is the [map] key.

The above code also uses method references. I refer to this line:

map.values()
   .forEach(Product::display); // method reference

Running the above code produces the following output:


4 gumboots Red_8 Bin 1 Sally 15
7 gumboots Red_8 Bin 1 Sue 16

3 gumboots Red_8 Bin 2 John 15

8 sandals Red_10 Bin 1 Bob 10

1 gumboots Blue_7 Bin 1 John 12
2 gumboots Blue_7 Bin 1 Sally 13
5 gumboots Blue_7 Bin 1 Sue 13
6 gumboots Blue_7 Bin 1 Bob 13

The output shows that Product with ID = 4 and ID = 7 are in the same group. Similarly, Product with IDs 1, 2, 5 and 6 are in the same group while ID = 3 is in a group by itself as is Product with ID = 8.

CodePudding user response:

Have you tried Java's streams API before? I think this may help you. You can filter each product using .filter() and reconstruct it back into a list or collection. For e.g:

List<Product> blue7 = productList.stream().filter(product -> product.attributeSetValue == "Blue_7").collect(Collectors.toList());

See this article for reference - https://www.baeldung.com/java-8-streams-introduction

CodePudding user response:

Since the attributes are strings, you could simply concatenate them and use that as a classifier:

Map<String, List<Product>> result = 
    productList.stream().collect(
            Collectors.groupingBy(p -> String.join(":", p.productName, p.attributeSetValue, p.storageBin)));

You could extract the joining part of the attribute values to a function to make it a bit more readable (but it is a matter of preference):

Function<Product,String> nameValueBin = p -> String.join(":", p.productName, p.attributeSetValue, p.storageBin);

Map<String,List<Product>> result2 = productList.stream()
                                               .collect(Collectors.groupingBy(nameValueBin));

with above approach you could easily add other functions if you need to group on other fields.

If not all attributes are of same type (for example if you need to group by productName(string), storageBin(string), quantityCounted(int), whatever (LocalDate...)) use a list instead of string concatenation:

productList.stream().collect(
            Collectors.groupingBy(p -> List.of(p.productName, p.attributeSetValue, p.storageBin)));
  •  Tags:  
  • java
  • Related