Reputation: 151
I am having two lists of Strings and want to compare the values in the lists and construct a new list of another object. I am able to do this with nested loops but looking for a more performant and neater solution.
List<String> list1 = new ArrayList();
list1.add("A,Airplane");
list1.add("B,Boat");
List<String> list2 = new ArrayList();
list2.add("A90, Boing Airplane");
list2.add("A70, Boing777");
list2.add("B80, Boing Boat");
There is a Vehicle object.
class Vehicle {
private String model;
private String submodel;
private String type;
private String subtype;
// setters getters
}
Now, I need to build the vehicle object using by matching the model (the first character from list1) with the first character in list2 and build something like this
private List<Vehicle> buildVehicle(List<String> list1, List<String> list2) {
List<Vehicle> vehicles = new ArrayList<>();
if (ObjectUtils.isNotEmpty(list2)) {
list2.forEach(v -> {
Vehicle vehicle = new Vehicle();
if (v.contains(",")) {
String[] val = StringUtils.split(v,",");
vehicle.setSubtype(val[0]);
vehicle.setSubmodel(val[1]);
}
for (String c : list1) {
//Matching the first character from element in list1
if (c.substring(0,1).equals(vehicle.getSubtype().substring(0,1))
&& c.contains(",")) {
String[] val = StringUtils.split(c, ",");
vehicle.setType(val[0]);
vehicle.setModel(val[1]);
}
break;
}
}
vehicles.add(vehicle);
});
}
return vehicles;
}
Can the nested loop be avoided by using streams?
Upvotes: 0
Views: 2387
Reputation: 40057
The data as provided.
List<String> list1 = new ArrayList<>();
list1.add("A,Airplane");
list1.add("B,Boat");
List<String> list2 = new ArrayList<>();
list2.add("A90, Boeing Airplane");
list2.add("A70, Boeing777");
list2.add("B80, Boeing Boat");
Regardless of how the model type information is obtained, this conversion might be more efficient and certainly easier if instead of using Lists
you used Maps
to hold the information. If the information was read in from a file it would be best to pre-process them (split if need be) as they are read in and put them in a map. If it was entered by hand then putting the information in a map would negate the need for any pre-processing at all. Here is a simple example.
Map<String,String> map1 = new HashMap<>();
map1.put("A","Airplane");
map1.put("B","Boat");
However, using the information as provided here is how I proceeded.
First, I created a Lambda to assist in the List conversion.
Function<List<String>, Map<String, String>> makeMap =
lst -> lst.stream().map(st -> st.split("\\s*,\\s*")).collect(
Collectors.toMap(a -> a[0], a -> a[1]));
// create the TypeModel map
Map<String, String> mapTM = makeMap.apply(list1);
// create the subTypeSubModel Map;
Map<String, String> mapSTSM = makeMap.apply(list2);
Now just use the keysets of each to go thru and piece it together.
I created a constructor in my example for Vehicle
which imho makes
a cleaner object creation.
List<Vehicle> vehicles = mapTM.keySet().stream()
.flatMap(type -> mapSTSM.keySet().stream()
.filter(subType -> subType.startsWith(type))
.map(sbType -> new Vehicle(mapTM.get(type),
mapSTSM.get(sbType), type, sbType)))
.collect(Collectors.toList());
vehicles.forEach(System.out::println);
Prints based on toString (which can be changed).
[Airplane, Boeing Airplane,A,A90]
[Airplane, Boeing777,A,A70]
[Boat, Boeing Boat,B,B80]
Here is the class sans setters and getters.
class Vehicle {
private String model;
private String submodel;
private String type;
private String subtype;
public Vehicle() {
}
public Vehicle(String model, String submodel, String type,
String subtype) {
this.model = model;
this.submodel = submodel;
this.type = type;
this.subtype = subtype;
}
public String toString() {
return "[" + String.join(", ", model, submodel, type, subtype)
+ "]";
}
}
Upvotes: 2
Reputation: 7745
I'd follow an approach like the following:
List<String> list1 = new ArrayList<>();
list1.add("A,Airplane");
list1.add("B,Boat");
List<String> list2 = new ArrayList<>();
list2.add("A90, Boing Airplane");
list2.add("A70, Boing777");
list2.add("B80, Boing Boat");
Pattern commaPattern = Pattern
.compile("\\s*,\\s*"); // a regex pattern to split by comma and the whitespace around it
Map<String, String> modelToType = list1.stream().map(commaPattern::split)
.collect(Collectors
.toMap(modelAndType -> modelAndType[0],
modelAndType -> modelAndType[1])); // mapping models to types for o(1) lookups
List<Vehicle> vehicles = list2.stream().map(commaPattern::split)
.map(subModelAndSubType -> {
Vehicle vehicle = new Vehicle();
vehicle.submodel = subModelAndSubType[0];
vehicle.subtype = subModelAndSubType[1];
vehicle.model = vehicle.submodel.substring(0, 1);
vehicle.type = modelToType.get(vehicle.model);
return vehicle;
}).collect(Collectors.toList());
Upvotes: 4