How to map values from List<String> into a List<Model> - arraylist

I'm practicing Java 8 features and I got to a problem that I couldn't solve:
I have a List of Strings that I need to map into a List of Customers, the List of Strings only has the name of the customers, but Customer Model has other properties:
public class Customer {
private int id{get; set;};
private String name{get; set;};
private String company{get; set;};
}
public static List<Customer> convertList() {
List<Customer> customerList = new ArrayList<Customer>();
List<String> nameList = new ArrayList<String>();
nameList.add("Customer A");
nameList.add("Customer B");
nameList.add("Customer C");
nameList.add("Customer D");
nameList.add("Customer E");
nameList.add("Customer F");
return customerList.stream()
.map()//here I got stuck
.collect(Collectors.toList());
}
What I want to do is to to set the value of the List into the Name property of the List, I tried using a stream and map but couldn't figure how to use it in this scenario.

List<Customer> customerList = nameList.stream().map(name -> {
Customer c = new Customer();
c.setName(name);
c.setId(generateId());
c.setCompany(getCompany(name));
return c;
}).collect(Collectors.toList());
You need to map the nameList to customerList so, you should stream on the nameList and convert it to customerList not the other way around.
Next, you need to figure out how to get the additional information like id, company etc using some external method/service if at all that is required during the map() operation.

Related

Unmarshell with customize deserializer using Jackson

i have a json string like this
{"jobs":[{"id":"1","state":"Visited","runTime":6224,"finalJobAssignment":{"owner":"10.107.181.236:5363",
"resourceId":"11",
"listingPositions":["11"]},"keys":1765624,"range":{"startPrefix":"aa",
"endPrefix":"bb"},"resources":[{"resourceId":"11"}]},{"id":"2","state":"Visited","runTime":6224,"finalJobAssignment":{"owner":"10.107.181.236:5363",
"resourceId":"22",
"listingPositions":["22","33"]},"keys":1765624,"range":{"startPrefix":"cc",
"endPrefix":"dd"},"resources":[{"resourceId":"22"}]}]}
im trying to deserialize using a custom deserializer, i only need resourceId, startPrefix, endPrefix and listingPositions(which is a list) from the json array, how do i get rid of "jobs" and return a list of my custom object?
custom object only has above useful fields
Currently i have something like this
private List<AuditEntry> deserialize(final JsonNode node,
final DeserializationContext deserializationContext) throws IOException {
List<AuditEntry> res = new ArrayList<>();
Iterator<JsonNode> iterator = node.get(JOBS_KEY).elements();
while (iterator.hasNext()) {
JsonNode jsonNode = iterator.next();
final String startKey = jsonNode.get(RANGE_KEY).get(START_PREFIX_KEY).asText();
final String endKey = jsonNode.get(RANGE_KEY).get(END_PREFIX_KEY).asText();
final String partitionId;
final String resourceId;
final List<JournalChapterAndSN> listingPositions;
final JsonParser listingPositionsParser;
resourceId = jsonNode.get(FINAL_JOB_ASSIGNMENT_KEY).get(RESOURCE_ID_KEY).asText();
listingPositionsParser = jsonNode.get(FINAL_JOB_ASSIGNMENT_KEY).get(LISTING_POSITIONS_KEY).traverse();
listingPositionsParser.nextToken();
List<String> listingPositionsString = deserializationContext.readValue(listingPositionsParser, List.class);
}
}
res.add(new AuditEntry(range, partitionId, listingPositions));
}
return res;
}
However when processing listingPositionString, DeserializationContext seems to be using my custom deserializer instead of the default one, resulting npe, how can i resolve this?

How to convert a nested list of objects into a map (Map<Integer, Map<Integer, Object>>) using lambdas/streams

I'm converting the following list of objects structure to a HashMap using two for loops as shown below.
public class Vehical {
private Category category;
private List<Brand> brandList;
public Category getCategory() {
return category;
}
public List<Brand> getBrandList() {
return brandList;
}
}
public class Category {
private Integer catId;
public Integer getCatId() {
return catId;
}
}
public class Brand {
private Model model;
public Model getModel() {
return model;
}
}
public class Model {
private List<Reg> regList;
public List<Reg> getRegList() {
return regList;
}
}
public class Reg {
private Integer regId;
public Integer getRegId() {
return regId;
}
}
public static void main(String[] args) {
//Assume that filled with data.***
List<Vehical> vehicalList = getVehicals();
Map<Integer, Map<Integer, Brand>> vehicalMap = new HashMap<Integer, Map<Integer, Brand>>();
for (Vehical vehical : vehicalList) {
Map<Integer, Brand> brandMap = new HashMap<Integer, Brand>();
for (Brand brand : vehical.getBrandList()) {
//Assume that zeroth index is always available and getting "RegId" from the zeroth element is fixed.***
brandMap.put(brand.getModel().getRegList().get(0).getRegId(), brand);
}
vehicalMap.put(vehical.getCategory().getCatId(), brandMap);
}
}
How can I do the same thing using lambdas/streams?
I tried with a flatMap, but it didn't work. Couldn't access the nested RegId while streaming.
Also, I tried with forEach but finally it looks likes the same for loop solution.
Map<Integer, Map<Integer, Brand>> vehicalMap = new HashMap<>();
vehicalList.forEach(v -> {
Map<Integer, Brand> brandMap = new HashMap<>();
v.getBrandList().stream().forEach(b -> brandMap
.put(b.getModel().getRegList().get(0).getRegId(), b));
vehicalMap.put(v.getCategory().getCatId(), brandMap);
});
Any help would be appreciated.
UPDATE
Working code sample based on #Nidhish Krishnan answer
Try this out
SOLUTION 1
If you want to get the result based on grouping, try this one
Map<Integer, Map<Integer, Brand>> vehicalMap = getVehicals().stream()
.collect(Collectors.groupingBy(vechiles -> vechiles.getCategory().getCatId(),
Collectors.collectingAndThen(
Collectors.groupingBy(vechile -> vechile.getBrandList().get(0).getModel().getRegList().get(0).getRegId()),
e ->e.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().get(0).getBrandList().get(0)))
)
))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
SOLUTION 2
If you don't want to use grouping try the below one
Map<Integer, Map<Integer, Brand>> vehicalMap = getVehicals().stream()
.collect(Collectors.toMap(
vechile -> vechile.getCategory().getCatId(),
vechile -> vechile.getBrandList().stream()
.collect(Collectors.toMap(
brands -> brands.getModel().getRegList().get(0).getRegId(),
brand -> brand, (a, b) -> b)),
(a, b) -> a));
UPDATE 1
Not sure what data you are having for vehicalList, this is how I created the test vehicalList
private static List<Vehical> getVehicals() {
return Lists.newArrayList(
new Vehical(new Category(21), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(100), new Reg(101), new Reg(102)))))),
new Vehical(new Category(22), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(200), new Reg(201), new Reg(202)))))),
new Vehical(new Category(23), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(300), new Reg(301), new Reg(302)))))),
new Vehical(new Category(24), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(400), new Reg(401), new Reg(402)))))),
new Vehical(new Category(25), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(500), new Reg(501), new Reg(502)))))),
new Vehical(new Category(26), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(600), new Reg(601), new Reg(602)))))),
new Vehical(new Category(26), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(700), new Reg(701), new Reg(702))))))
);
}
Let say, I have entity class TableColumnInfo
public class TableColumnInfo {
#Column(name = "table_name")
private String tableName;
#Column(name = "column_name")
private String columnName;
#Column(name = "data_type")
private String dataType;
#Column(name = "is_nullable")
private Boolean isNullable;
#Column(name = "column_default")
private String columnDefault;
}
In input, I am given List of Object as below:
List<TableColumnInfo> tableColumnInfoRes = some database repository call;
In output, I want nested MAP as below:
Map<String, Map<String, TableColumnInfo>>
Where:
Outter Map Key: String i.e. tableName
Outter Map Value: Map
Inner Map Key: String i.e. columnName
Inner Map Value: Object i.e. TableColumnInfo
Constraints:
Outter map tableName and Inner map column name must belong to same object.
It means, for every tableName in outter map, inner map must have a column name as a key and entire object as a value (1:1 Mapping)
Solution:
Using Java Functional programming and Collections
Below BiFunction used to generate inner Map
BiFunction<List<TableColumnInfo>, String, Map<String, TableColumnInfo>> mapper1 =
(infoList, tableName2) ->
{ Map<String, TableColumnInfo> res =
infoList.stream().filter(infoList2 -> infoList2.getTableName().equals(tableName2))
.collect(Collectors.toMap(TableColumnInfo::getColumnName, Function.identity()));
return res;
};
Below code used to produce outter map
Map<String, Map<String, TableColumnInfo>> tableColumnMap = tableColumnInfoRes.stream()
.collect(Collectors.toMap(
tableInfo -> tableInfo.getTableName(),
tableInfo -> mapper1.apply(tableColumnInfoRes,tableInfo.getTableName()),
(a,b)->b
));
In above code, BIFunction is called for every table name.
In Inner Map, to produce value as object, make use of Function.identity()
For outter map, to avoid conflict of key, make use of (a,b) -> b
Constraints are satisfied in BiFunction using filters on stream.

JavaFX - Using a ChangeListener on a ComboBox to populate another ComboBox with an ArrayList results in the ArrayList being empty

I posted this question two days ago about attaching a ChangeListener to a ComboBox to dictate what output would be shown on a second ComboBox. To put this in short, the idea was that the first ComboBox shows some Unit Types, and depending on the Unit Type chosen would depend on the list of Units shown in the second ComboBox based on the Unit Type chosen. For example If you chose the "Elites" Unit Type in the first ComboBox the second ComboBox should be populated with all the "Elite" units.
Now the person who answered my original question helped a lot with the code in the ChangeListener and I did manage to get it working. However, currrently it only works if you add one single unit at a time to the Unit Type. Ideally I'd want to add an ArrayList or some other suitable data structure of units to the Unit Type, rather than individual units. This would cut down on code, and be more efficent.
So as you can see in this code snippet, currently the elites Unit Type addUnit method only accepts single strings at a time,
elites = new Unit_Type("Elites");
elites.addUnit("Dreadnought");
elites.addUnit("Ironclad Dreadnought");
where as I would like it so i could add an ArrayList or simular data structure to the elites Unit Type addUnit method, like this code snippet:
elitesList = createArrayList("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard","Sternguard Veteran", "Terminator", "Vanguard Veterans");
elites = new Unit_Type("Elites");
elites.addUnit(elitesList);
Note that the elitesList is generated by a helper method, as there are 8 other lists like this each representing a different Unit Type.
Now i've tried changing code in the Unit Type class, to have the units ArrayList be of type ArrayList<String> and also trying to change the addUnit method to accept a parameter of an ArrayList<String> but whenever i run the program, selecting a Unit Type in the first ComboBox results in an empty array in the second ComboBox.
Here is the rest of the code of both the AddUnitPane class (view), and the Unit_Type class (model)
AddUnitPane Class
public class AddUnitPane extends GridPane
{
private Label unitTypeLbl, unitLbl, squadNameLbl, squadSizeLbl;
private ComboBox<Unit_Type> unitTypeCombo;
private ComboBox<String> unitCombo;
private ComboBox<Integer> squadSizeCombo;
private TextField squadNameTf;
private Button addSquadBtn;
private ArrayList<String> elitesList, fastAtkList, heavySptList, hqList, lordsowList, specialCList, transportList, troopsList; //Declare the sublists that hold all the units for the type of unit
Unit_Type elites, fastAttk, heavySpt, hQ, lordsOW, specialC, transport, troops;
public AddUnitPane()
{
this.setVgap(15);
this.setHgap(20);
this.setAlignment(Pos.CENTER);
ColumnConstraints col1 = new ColumnConstraints();
col1.setHalignment(HPos.RIGHT);
this.getColumnConstraints().add(col1);
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
elitesList = createArrayList("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard",
"Sternguard Veteran", "Terminator", "Vanguard Veterans");
fastAtkList = createArrayList("Attack Bike", "Stormhawk Interceptor", "Stormtalon Gunship", "Assault", "Bike", "Land Speeder", "Scout Bike");
heavySptList = createArrayList("Hunter", "Land Raider Crusader", "Land Raider Redeemer", "Land Raider", "Predator", "Stalker", "Stormraaven Gunship", "Vindicator",
"Whirlwind", "Centurion Devastator", "Devastator", "Thunderfire Cannon");
hqList = createArrayList("Captain", "Chaplain", "Librarian", "Techmarine");
lordsowList = createArrayList("Marneus Calger", "Roboute Guilliman");
specialCList = createArrayList("Antaro Chronus", "Cato Sicarius", "Ortan Cassius", "Torias Telion", "Varro Tigurius");
transportList = createArrayList("Drop Pod", "Land Speeder Storm", "Razorback", "Rhino");
troopsList = createArrayList("Scout", "Tactical");
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
elites = new Unit_Type("Elites");
//elites.addUnit(elitesList);
elites.addUnit("Dreadnought");
elites.addUnit("Ironclad Dreadnought");
fastAttk = new Unit_Type("Fast Attack");
fastAttk.addUnit("Attack Bike");
fastAttk.addUnit("Stormhawk Interceptor");
ObservableList<Unit_Type> unitTypeOList = FXCollections.observableArrayList(elites, fastAttk); //add each Unit_Type to an Observable List
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
unitTypeLbl = new Label("Select The Unit Class: "); //Initialise all of the labels
unitLbl = new Label("Select The Unit: ");
squadNameLbl = new Label("Squad Name: ");
squadSizeLbl = new Label("Squad Size");
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
unitTypeCombo = new ComboBox<Unit_Type>(); //Initialise the unitTypeCombo (first ComboBox)
unitTypeCombo.setItems(unitTypeOList); //Populate the unitTypeCombo with the UnitTypeOList (observable list) from line 82
//unitTypeCombo.getSelectionModel().selectFirst(); //Set the unitTypeCombo to show the first item
unitCombo = new ComboBox<>(); //Initialise the unitCombo (second ComboBox)
unitTypeCombo.valueProperty().addListener(new ChangeListener<Unit_Type>()
{
#Override
public void changed(ObservableValue<? extends Unit_Type> observable, Unit_Type oldValue, Unit_Type newValue)
{
unitCombo.setItems(newValue == null ? FXCollections.emptyObservableList() : newValue.getUnitsForType());
}
});
squadNameTf = new TextField();
squadSizeCombo = new ComboBox<>();
addSquadBtn = new Button("Add Squad");
this.add(unitTypeLbl, 0, 1);
this.add(unitTypeCombo, 1, 1);
this.add(unitLbl, 0, 2);
this.add(unitCombo, 1, 2);
this.add(squadNameLbl, 0, 3);
this.add(squadNameTf, 1, 3);
this.add(squadSizeLbl, 0, 4);
this.add(squadSizeCombo, 1, 4);
this.add(new HBox(), 0, 5);
this.add(addSquadBtn, 1, 5);
}
public void AddUnitHandler(EventHandler<ActionEvent> handler)
{
addSquadBtn.setOnAction(handler);
}
private static <T> ArrayList<T> createArrayList(T... items) //generates the unit lists
{
ArrayList<T> result = new ArrayList<>(items.length);
for (T item : items)
{
result.addAll(result);
}
return result;
}
}
Unit_Type Class
public class Unit_Type implements Serializable
{
private String typeName;
private ArrayList<String> units; //a unit type is an aggregation of units. Tried changing this type to ArrayList<String>
public Unit_Type(String typeName)
{
this.typeName = typeName;
units = new ArrayList<>();
}
public void addUnit(String u) //tried changing this parameter to ArrayList<String>
{
units.add(u);
}
public void setTypeName(String name)
{
typeName = name;
}
public ObservableList<String> getUnitsForType() //method used in the ChangeListener in the AddUnitPane class
{
ObservableList unitsOList = FXCollections.observableArrayList(units);
return unitsOList;
}
#Override
public String toString() //allows the ComboBox to display values correctly
{
return typeName;
}
}
I wouldn't store the backing list in the Unit_Type class. It would be a better idea to store the ObservableList in a field, write a custom serialisation method and make the field transient.
this way you could also initialize the unit types using
elites.getUnitsForType().addAll("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard",
"Sternguard Veteran", "Terminator", "Vanguard Veterans");
but it's probably even shorter to initialize the list in the constructor:
public class Unit_Type implements Serializable {
private String typeName;
private transient ObservableList<String> units;
private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
// serialize units list as string array
stream.writeObject(units.toArray());
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
// read array from stream and initialize list with it
// Note: because of the way we write objects of this type we can use the raw type here safely
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());
}
public Unit_Type(String typeName, String... units) {
this.typeName = typeName;
this.units = FXCollections.observableArrayList(units);
}
public void setTypeName(String name) {
typeName = name;
}
public ObservableList<String> getUnitsForType() {
return units;
}
#Override
public String toString() {
return typeName;
}
}
elites = new Unit_Type("Elites",
"Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought",
"Assault Terminator", "Centurion Assault", "Command",
"Honour Guard", "Sternguard Veteran", "Terminator",
"Vanguard Veterans"
);
During (de)serialisation everything except the ObservableList is handled using the default serialisation mechanism.
stream.defaultWriteObject();
stream.defaultReadObject();
The transient keyword causes the field to be ignored (ObservableLists are not serializable in general, since it would be hard/impossible to persist the listeners especially if they reference UI elements). In this case we simply read/write the strings in the list as array:
stream.writeObject(units.toArray());
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());
For more details see the Customize the Default Protocol section here: http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html

Put "List of Objects" in KrollDict with Titanium Android Module Development

I have a custom class declared in the module source code.
public class Friend{
public String name;
public List<String> phoneNumbers;
public List<String> emailAddresses;
public Friend(String name, List<String>emailAddresses,
List<String>phoneNumbers){
this.name = name;
this.emailAddresses = emailAddresses;
this.phoneNumbers = phoneNumbers;
}
}
I declared an Android Method in the Module
#Kroll.method
protected synchronized void getListOfObjects(){
List<String> emailAddresses = Arrays.asList("email1#yahoo.com", "email2#yahoo.com", "email3#yahoo.com");
List<String> phoneNumbers = Arrays.asList("1", "2", "3");
List<Friend> friendList = new ArrayList<Friend>();
friendList.add(new Friend("test1", emailAddresses, phoneNumbers));
friendList.add(new Friend("test2", emailAddresses, phoneNumbers));
friendList.add(new Friend("test3", emailAddresses, phoneNumbers));
KrollDict kd = new KrollDict();
kd.put("friendList", friendList);
if (hasListeners("onShow")) {
fireEvent("onShow", kd);
}
}
Upon Calling the getListOfOjects method in the Titanium App
module.getListOfObjects();
module.addEventListener('onShow', function(e){
Ti.API.info(JSON.stringify(e.friendList));
});
I cant seem to retrieve the friendList object.
The EXPECTED RESULT that I wanted to achieve would be like this
[
{test1, ["email1#yahoo.com", "email2#yahoo.com", "email3#yahoo.com"], ["1", "2", "3"]},
{test2, ["email1#yahoo.com", "email2#yahoo.com", "email3#yahoo.com"], ["1", "2", "3"]},
{test3, ["email1#yahoo.com", "email2#yahoo.com", "email3#yahoo.com"], ["1", "2", "3"]}
]
The question is, HOW TO ACHIEVE THE EXPECTED RESULT BASED ON THE SAMPLE CODES ABOVE?
Convert the List object into JSON string using GSON and assign the result string to the KrollDict property
KrollDict kd = new KrollDict();
Gson gson = new Gson();
String friendListStr = gson.toJson(friendList);
kd.put("friendList", friendListStr);
Another option is to return an object array like this:
KrollDict kd = new KrollDict();
Object[] obj = new Object[friendList.size()];
for (int i=0; i< friendList.size(); ++i){
KrollDict model = new KrollDict();
model.put("name", friendList.get(i).name);
// ...
obj[i] = model;
}
kd.put("list", obj);
fireEvent("onShow", kd);
That way you'll have an array in your event and don't need to convert that string into json later on.
If you want to use a JSON you can use TiConvert with toJSON, toJSONArray or toJSONString depending on your parameter. No need for Gson.

How to retrieve mongodb field value stored as array of string into a java ArrayList

Document structure is:
db.lookupdata.insert({ parent_key : "category" , key : "accessories" , value : ["belts","cases","gloves","hair","hats","scarves","sunglasses","ties","wallets","watches"]})
i want to store array filed values in java array list
i am finding the document like this:
FindIterable<Document> iterable1 = docCollectionLookup.find(Filters.eq("parent_key", "category"));
Iterator<Document> iter1=iterable1.iterator();
while(iter1.hasNext())
{
Document theObj = iter1.next();
categotyLookUpMap.put(theObj.getString("key"), list);
}
now here how can i retrieve array field values(key:"value") in ArrayList
You can retrieve array field values(key:"value") in ArrayList just like how you retrieve string field key. Please refer below:
FindIterable<Document> iterable1 = docCollectionLookup.find(Filters.eq("parent_key", "category"));
Iterator<Document> iter1=iterable1.iterator();
//Create a HashMap variable with type <String,ArrayList>,according to your needs
Map<String,ArrayList> categotyLookUpMap = new HashMap<String,ArrayList>();
while(iter1.hasNext())
{
Document theObj = iter1.next();
//Get method of Document class will return object,parse it to ArrayList
categotyLookUpMap.put(theObj.getString("key"), (ArrayList)theObj.get("value"));
}
Alternatively, you can use Morphia which is MongoDB object-document mapper in Java. You can setup dependency / download JAR from here
First, create LookupData class to map to lookupdata collection. Annotation #Id is required else will throw exception with message "No field is annotated with #Id; but it is required". So create an _id field for it.
#Entity("lookupdata")
public class LookupData {
#Id
String _id ;
#Property("parent_key")
String parentKey;
String key;
ArrayList<String> value;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getParentKey() {
return parentKey;
}
public void setParentKey(String parentKey) {
this.parentKey = parentKey;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public void setValue(ArrayList<String> value) {
this.value = value;
}
public ArrayList<String> getValue() {
return value;
}
}
Retrieve array field values as below:
MongoClient mongoClient = new MongoClient(new MongoClientURI("mongodb://localhost"));
Morphia morphia = new Morphia();
morphia.map(LookupData.class);
//lookupdata collection is under my local db "tutorials" in this case
Datastore datastore = morphia.createDatastore(mongoClient, "tutorials");
Map<String,ArrayList> categotyLookUpMap = new HashMap<String,ArrayList>();
LookupData lookupData = datastore.find(LookupData.class).get();
categotyLookUpMap.put(lookupData.getKey(), lookupData.getValue());