Java transform ArrayList of type object to another ArrayList of type object

Viewed 165

I have a class with member variables:

class Filter{
  private String key;
  private String operator;
  private Object value;
}

And List of Type Filter as below:

[
{
  key: status,
  operator:equal,
  value: active
},
{
  key: status,
  operator:equal,
  value: inactive
},
{
  key: name,
  operator:equal,
  value: jhon
},
{
  key: id,
  operator:equal,
  value: 123
}
] 

I want to transform the above List into a new List as below where if the key is same key: status then in new list need to have only one entry with value is in list type value: [active, inactive] and operator will be IN

[
{
  key: status,
  operator:in,
  value: [active, inactive]
},
{
  key: name,
  operator:equal,
  value: jhon
},
{
  key: id,
  operator:equal,
  value: 123
}
]
 

What is the best and efficient way to achieve this solution, preferably using Java 8 stream and lambdas? Wanted to avoid traditional for loop.

2 Answers

You can use Collectors.partitioningBy to split the List into two parts based on whether or not the Filter's key is "status". Demo

final Map<Boolean, List<Filter>> parts = list.stream()
        .collect(Collectors.partitioningBy(f -> "status".equals(f.getKey())));
final Object[] statuses = parts.get(true).stream().map(Filter::getValue).toArray();
final List<Filter> result = parts.get(false);
if (statuses.length != 0) {
    result.add(0, new Filter("status", "in", statuses));
}

For multiple keys, you can use Collectors.groupingBy. Demo

final Map<String, List<Filter>> map = list.stream().collect(Collectors.groupingBy(Filter::getKey, LinkedHashMap::new, Collectors.toList()));
final List<Filter> result = map.entrySet().stream()
        .map(e -> e.getValue().size() > 1
                ? new Filter(e.getKey(), "in", e.getValue().stream().map(Filter::getValue).toArray())
                : e.getValue().get(0))
        .collect(Collectors.toList());

First, create a class for your response with List of value

class FilterRes{
  private String key;
  private String operator;
  private List<Object> values;
  // getter setter constractor
}

Now first transform Filter into FilterRes using map() then collect as map using Collectors.toMap and merge group in merge function. Then get the values in an ArrayList

Map<Integer, FilterRes> resMap = list.stream()
                .map(s -> new FilterRes(s.getKey(), s.getOperator(),
                                        new ArrayList<>(Arrays.asList(s.getValue()))))
                .collect(Collectors.toMap(FilterRes::getKey, e -> e,    
                                    (a, b) -> new FilterRes(a.getKey(), "in", 
                                                Stream.concat(a.getValue().stream(),
                                                              b.getValue().stream())
                                                         .collect(Collectors.toList()))));

List<FilterRes> resList= new ArrayList(resMap.values()); // Create list from map values
Related