I have List<Person> object and would like to convert this in to Map<Integer, List<Person>> where the key of the map denotes a person's property grade. Its possible to have multiple Person object with the same grade in the source list, in that case i want to group them all in to List against the corresponding grade in the resulting Map.
I have the below code, so far
public class PersonMain
{
public static void main(String[] args)
{
Person person1 = new Person();
person1.setName("person1");
person1.setGrade(1);
Person person2 = new Person();
person2.setName("person2");
person2.setGrade(2);
Person person3 = new Person();
person3.setName("person3");
person3.setGrade(1);
List<Person> persons = Arrays.asList(person1, person2, person3);
Map<Integer, List<Person>> personsMap = persons.stream()
.collect(Collectors.toMap(Person::getGrade, PersonMain::getPerson, PersonMain::combinePerson));
System.out.println(personsMap);
}
private static List<Person> getPerson(Person p)
{
List<Person> persons = new ArrayList<>();
persons.add(p);
return persons;
}
private static List<Person> combinePerson(List<Person> oldVal, List<Person> newVal)
{
oldVal.addAll(newVal);
return oldVal;
}
}
Is there a better way of implementing this?
Your current solution is fine, but it's more intuitive to do it with the groupingBy collector:
Map<Integer, List<Person>> personsMap =
persons.stream()
.collect(Collectors.groupingBy(Person::getGrade));
This overload of the groupingBy collector is pretty straightforward as all it does is take a classifier (Person::getGrade) function which will extract the key to group the objects of the stream by.
Much simpler solution if using Java 8:
Map<Integer, List<Person>> map = persons.stream()
.collect(Collectors.groupingBy(Person::getGrade));
Related
I have a class called Result (data that I want to show in my table) with several attributes.
public class Result
{
private final SimpleStringProperty object;
private List<SimpleStringProperty> listObject= new ArrayList<SimpleStringProperty>();
private final Util util = new Util();
public Result(String object, String[] listObject)
{
this.object= new SimpleStringProperty(object);
this.objectList= util.transformar(listObject);
}
public String getObject()
{
return this.object.get();
}
public void setObject(String object)
{
this.hash.set(hash);
}
public String[] getListObject()
{
return util.transformar2(this.listObject);
}
public void setListObject(String[] listObject)
{
this.listObject= transformar(listObject);
}
}
And I have my controller where I have an empty table and I add the columns and the objetcs Result. The controller has the attribute:
#FXML
private TableView tablaResultado;
The method were I do that is:
private List<TableColumn> load()
{
List<TableColumn> listColumn = new ArrayList<TableColumn>();
Object2 object2= new Object2();
List<Result> listaResultado = object2.getResultado();
ObservableList<Result> prueba = FXCollections
.observableArrayList(listaResultado);
TableColumn<Result, String> object= new TableColumn<Result, String>("object");
direccion.setCellValueFactory(
new PropertyValueFactory<Result, String>(
"object"));
listColumn.add(object);
List<TableColumn<Result, String>> listObject = new ArrayList<TableColumn<Result, String>>();
for (int i = 0; i < ((Result) listaResultado
.get(0)).getListObject().length; i++)
{
TableColumn<Result, String> columna = new TableColumn<Result, String>(
this.lenguaje.getProperty("listObject"+i);
columna.setCellValueFactory(
new PropertyValueFactory<Result, String>(
"listObject[" + i + "]"));
objectList.add(columna);
}
listColumn.addAll(objectList);
ObservableList<TableColumn<Result, String>> prueba2 = FXCollections
.observableArrayList(listObject);
this.tablaResultado.setItems(prueba);
this.tablaResultado.getColumns().addAll(object);
for (int i = 0; i < prueba2.size(); i++)
{
this.tablaResultado.getColumns().addAll(prueba2.get(i));
}
}
return listColumn ;
The result is the columns with their names and the data in the column object but the data in the columns listObject is empty. It has to be a list or something because I don't know the size of listObject.
If I change:
columna.setCellValueFactory(new PropertyValueFactory<Result, String ("listObject[" + i + "]"));
and I write:
columna.setCellValueFactory(new PropertyValueFactory<Result, String ("listObject"));
I got something like Ljava.lang.String;#bda3303 in that columns.
Do
final int index = i ;
columna.setCellValueFactory(cellData ->
new SimpleStringProperty(cellData.getValue().getListObject()[index]));
PropertyValueFactory does not "evaluate" an expression. It just tries to access a property of the table item. The default TableCells will use the toString method to get the text to display, which is why you get something like Ljava.lang.String;#bda3303 in the third version, see Java arrays printing out weird numbers, and text .
However you can write your own cellValueFactory:
public class Result
public StringProperty listObjectProperty(int index) {
return listObject.get(index);
}
final int columnIndex = i;
columna.setCellValueFactory(cellData -> cellData.getValue().listObjectProperty(columnIndex));
Alternatively simply create a ObservableValue<String> with a constant value for the column value:
final int columnIndex = i;
columna.setCellValueFactory(cellData -> Bindings.createStringBinding(() -> cellData.getValue().getListObject()[columnIndex]));
I have been looking through java's api of Map for possible reason why a certain Map (map1) in my code gets updated as well when I update another map (map2) or maybe something is wrong about how I wrote it.
void process(Object superObject) {
Map<Date, Object> map1 = superObject.getValuesForMap();
Map<Date, Object> map2 = map1;
updateValueOf(superObject,map2);
}
This is how I updated the value of map2.
updateValueOfMap(Object superObject,Map<Date, Object> map2){
List<Object> objects = getTheObjectsFromASource;
for (Object obj : objects) {
List<Triple<Date, Double, Object>> triples = superObject.getSomeEntriesWithThisAttribute(obj.getCertainAttrib());
for (Triple<D,D,O> t : triples) {
Object cache = map2.get(t.first)
if (cache == null) {
cache = new Object();
cache.setThis(t.second);
cache.setThat(t.third);
} else {
Double value = cache.getThis() + t.second; // add the double value from triple to the current cache Object's value
cache.setThis(value); // and update the Object's value in the map
}
map2.put(t.first, cache);
}
}
}
The problem is certain entries in superObject.getValuesForMap() gets updated too with the same value as the corresponding entries in map2 every iteration in the for (Triple..). Why is that so?
Responses will be greatly appreciated. Thanks in advance!
Map map1 = superObject.getValuesForMap();
Map map2 = map1;
All three above , points to the same memory location, so it will indeed be updated.
Try this way :
Map map2 = new HashMap();
map2.putAll(map1);
UPDATE : Sample program below(map1 not updated with MAP2 changes.)
public class BaseClass {
Map<String,String> xx = new HashMap<String,String>();
public BaseClass(){
xx.put("1", "One");
xx.put("2", "Two");
xx.put("3", "Three");
}
public Map<String,String> getValuesForMap(){
return xx;
}
}
public class TestProgram extends BaseClass{
void process() {
Map<String, String> map1 = getValuesForMap();
Map<String, String> map2 = new HashMap<String,String>();
map2.putAll(map1);
updateValueOf(map1, map2);
}
public void updateValueOf(Map<String, String> map1, Map<String, String> map2){
String str1 = map2.get("1");
str1 = str1+"Item";
map2.put("1", str1);
String str2 = map2.get("2");
str2 = str2+"Item";
map2.put("2", str2);
String str3 = map2.get("3");
str3 = str3+"Item";
map2.put("3", str3);
System.out.println("Printing Map1 ");
printit(map1);
System.out.println("Printing Map2 ");
printit(map2);
System.out.println("Printing Map1 Again");
printit(map1);
System.out.println("Printing Map2 Again");
printit(map2);
}
public void printit(Map<String,String> map){
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry pairs = (Map.Entry)iter.next();
System.out.println(pairs.getKey() + " = " + pairs.getValue());
}
}
public static void main(String[] args){
TestProgram ts = new TestProgram();
ts.process();
}
}
I'm not so good at javafx and also in English^^. I programmed a TableView but I don't know why it doesn't fill the Table with the Items I created. Perhaps it fills the Table, but I don't see them. I hope somebody could help me here out. Here is the Code:
private final TableView<VerzeichnisDaten> table = new TableView();
private final ObservableList<VerzeichnisDaten> data = FXCollections.observableArrayList();
TableColumn titleCol = new TableColumn("Titel");
TableColumn lastNameCol = new TableColumn("Nachname");
TableColumn firstNameCol = new TableColumn("Vorname");
TableColumn TelCol = new TableColumn("Telefon");
TableColumn FaxCol = new TableColumn("Fax");
TableColumn EmailCol = new TableColumn("E-Mail");
// here is a loop which wait for a mouse click on an item
// item will be saved in abteilung
try {
VD.mitarbeiter(abteilung);
} catch (SQLException ex) {
/* exception */);
}
centerPane.getChildren().clear();
table.getColumns().clear();
data.clear();
for(int j = 0;j < VD.count_mi;j++) {
data.add(new VerzeichnisDaten(VD.title_speicher[j], VD.lname_speicher[j], VD.fname_speicher[j], VD.tel_speicher[j], VD.fax_speicher[j],VD.email_speicher[j] ));
}
titleCol.setMinWidth(100);
titleCol.setCellValueFactory (new PropertyValueFactory<VerzeichnisDaten, String>("Titel"));
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory (new PropertyValueFactory<VerzeichnisDaten, String>("Nachname"));
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory (new PropertyValueFactory<VerzeichnisDaten, String>("Vorname"));
TelCol.setMinWidth(100);
TelCol.setCellValueFactory (new PropertyValueFactory<VerzeichnisDaten, String>("Telefon"));
FaxCol.setMinWidth(100);
FaxCol.setCellValueFactory (new PropertyValueFactory<VerzeichnisDaten, String>("Fax"));
EmailCol.setMinWidth(100);
EmailCol.setCellValueFactory (new PropertyValueFactory<VerzeichnisDaten, String>("E-Mail"));
table.setItems(data);
table.getColumns().addAll(titleCol,lastNameCol,firstNameCol,TelCol,FaxCol,EmailCol);
centerPane.getChildren().addAll(table);
mainPane.setCenter(centerPane);
}
primaryStage.setScene(new Scene(mainPane, 855, 400));
primaryStage.show();
Here is the Class VerzeichnisDaten:
String[] title_speicher, lname_speicher, fname_speicher, tel_speicher, fax_speicher, email_speicher;
SimpleStringProperty title, lastName, firstName, Tel, Fax, Email;
public VerzeichnisDaten (String title, String lname, String fname, String tel, String fax, String email) {
this.title = new SimpleStringProperty(title);
this.lastName = new SimpleStringProperty(lname);
this.firstName = new SimpleStringProperty(fname);
this.Tel = new SimpleStringProperty(tel);
this.Fax = new SimpleStringProperty(fax);
this.Email = new SimpleStringProperty(email);
}
// Setter and Getter are now implemented, only not shown
this code belongs to VerzeichnisDaten. above them was more code but is
not relevant now.
void mitarbeiter (String Abteilung) throws SQLException {
// more code ...
stmt = conn.createStatement();
rset = stmt.executeQuery(sql_mi_stmt);
i = 0;
while (rset.next()){
title_speicher[i] = rset.getString("title");
lname_speicher[i] = rset.getString("lname");
fname_speicher[i] = rset.getString("fname");
tel_speicher[i] = rset.getString("tel");
fax_speicher[i] = rset.getString("fax");
email_speicher[i] = rset.getString("email");
i = i + 1;
}
stmt.close();
}
The string that you supply to the PropertyValueFactory, e.g. as here:
new PropertyValueFactory<VerzeichnisDaten, String>("Titel")
must match the variable name of the property in your data class.
That is, if you have:
class Person {
StringProperty firstNameProperty; // note: must end in "Property", per convention
StringProperty lastNameProperty;
}
the respective property value factories would be:
new PropertyValueFactory<Person, String>("firstName") // property name sans "Property" postfix
new PropertyValueFactory<Person, String>("lastName")
In your code, you 1) have property names that do not end with "Property", i.e. you have:
SimpleStringProperty title, lastName, firstName, Tel, Fax, Email;
and 2) you have used the names of the columns headers instead of the property names in your property value factories.
And thus you have discovered the joys of string-based, or type-unsafe in general, programming: the compiler is happy, but nothing works.
The PropertyValueFactory supplied with JavaFX is at best a hack, and more generally a bad practice. If you look at its Javadoc, you see exactly what it does: it provides a simpler way of creating a value factory instead of having to deal with the ugly Callback SAM, by using reflection and special naming conventions (the latter to which you were victim) to find the right property value.
With Java 8's lambda syntax you can write those value factories in a much simpler way and I would disadvice the usage of PropertyValueFactory.
All, Say you have a list of object which class is named Person.
public class Person
{
public string name{get;set;}
public string id{get;set;}
public int age{get;set;}
public Major major{get;set;}
}
public class Major{
public string MajorName{get;set;}
public string MajorId{get;set;}
}
And Say you have a ListBox which id is personListBox. and It is trying to bind with the List of Person.
List<Person> list= new List<Person>();
list.add(...);
...
list.add(...);
personListBox.DataSource=list;
personListBox.DataTextField = "name";
personListBox.DataValueField = "id";
personListBox.DataBind();
But My question is How can I convert the selected Item to Person?
What I image the code looks like below.
protected void personListBox_SelectedIndexChanged(object sender, EventArgs e)
{
//Person item = (Person)lbBizCategory.SelectedItem;
//string majorName = item.major.MajorName;
}
Unfortunatedly, It doesn't work. Is there any way to make it ? thanks.
You probably still have a reference to your list. Try
protected void personListBox_SelectedIndexChanged(object sender, EventArgs e)
{
Person yourPerson = list.First(x => x.id == int.Parse(personListBox.SelectedValue));
}
We cannot directly typecast the selectedItem to the Person type..!!
Instead, we can have a Person object created
and assign the value of the object's property to the selectedItem, which is like:
Person person=new Person();
person.name=personListBox.SelectedItem.ToString();
By the way, we cannot add values to the list as you wrote:
List list= new List();
list.add(...);
Since list can now accept only Person type; so we need to create person object,
fill the object's properties and later add the object to the list as:
Person person=new Person();
person.name="sagar";
person.id=1;
....
list.add(person);
For example, I have an instance of the following class declaration:
public class Person
{
public string Name = "";
public Dictionary<string, string> Properties = new Dictionary<string, string>();
}
And I have a list of person that I wish to bind to an Asp.NET drop down list.
List<Person> people = new List<Person>();
//fill the list etc..
//bind to the drop down list
ddlPeople.DataSource = people;
ddlPeople.DataTextField = "Name";
ddlPeople.DataTextField = "Properties['Age']"; //this is what I would like!
Age is always present. I do not have control of the person class. Does anyone know if what I am trying to do is achievable?
Thanks!
As far as I know, you can not do that.
I guess I would go for :
ddlPeople.Items.Clear();
ddlPeople.Items.AddRange(people.Select(p => new ListItem(p.Name, p.Properties["Age"])).ToArray());
But I am not sure it is the point of your question.
Perhaps you could just create a read-only property on your person class?
public class Person
{
public string Name = "";
public Dictionary<string, string> Properties = new Dictionary<string, string>();
public int Age
{
get
{
// ... code to handle situations where Properties
// is null or does not contain key
return (int)this.Properties["Age"];
}
}
}
I believe the kind of binding you're seeking is possible in WPF, but I don't think I've ever seen it done in ASP.NET.