Creating objects dynamically using a list of Strings? - java

I am trying to create objects by using a list of Strings that will populate their fields. For example I have the list of strings, Note that the values repeat after every 3. i.e. id, name , address.
List<String> myList = "Id1", "name1", "address1", "Id2", "name2", "address2";
I would like to dynamically create a number of Person Objects (shown below) using this list
Person object:
public class Person {
private String id;
private String name;
private String address;
public Person() {
}
public Person(String id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
//standard getters and setters
}
What I want to do is have a method that will take the list of strings as an input and then create the objects dynamically. How could I best do this?
I know that I could do the following if I knew that I was definitely populating 2 objects, but the problem is that there may be more or less.
public List<Person> createObjectsFromStringList(List<String> list){
List<person> personList = new Arraylist<>();
Person person1 = new Person(list.get(0), list.get(1), list.get(2));
Person person2 = new Person(list.get(3), list.get(4), list.get(5));
personList.add(person1);
personList.add(person2);
return personList;
}

A simple for loop can do the work:
public List<Person> createObjectsFromStringList(List<String> list) {
List<person> personList = new Arraylist<>();
//We use < size-2 here because we access 2 indeces ahead of x in this loop
for(int x=0; x<list.size()-2; x+=3) {
personList.add(new Person(list.get(x), list.get(x+1), list.get(x+2));
}
return personList;
}
At first glance, I feel like having the different field values across one List is a sign of a poor code structure, but maybe you're already stuck with this List as-is.
Edit:
Now let's suppose you want a partial Person based on the number of remaining elements. Supposing they are still in the same order, you could modify this method to check the validity of the current index for each field:
public List<Person> createObjectsFromStringList(List<String> list) {
List<person> personList = new Arraylist<>();
int size = list.size();
//Now we remove the "-2" from size check because we will handle this ourselves
for(int x=0; x<size; x+=3) {
String id = list.get(x); //Obviously valid
String name = x+1 < size? list.get(x+1) : null;
String address = x+2 < size? list.get(x+2) : null;
personList.add(new Person(id, name, address);
}
return personList;
}
We're using the ternary operation ? ... : null here, so if we run out of elements we set the associated Person field to null instead of using an out-of-bounds index.

You can use recursion
public List<Person> createObjectsFromStringList(List<String> list){
List<person> personList = new Arraylist<>();
for(int i=0; i<list.size(); i++){
personList.add(new Person(list(i),list(i+1),list(i+2)));
i+=2;
}
return personList;
}
Notice that restructuring your list would me much better. make it like this:
List<String> myList = "Id1_name1_address1", "Id2_name2_address2";
Or even use different lists (it is much better). If you change your list structure as above then change the code to this :
public List<Person> createObjectsFromStringList(List<String> list){
List<person> personList = new Arraylist<>();
for(int i=0; i<list.size(); i++){
String[] info= list(i).split("_"); // this will give u a 3element array of yout info IdX nameX addressX
personList.add(new Person(info(0),info(1),info(2)));
}
return personList;

As you want to access your elements sequentially you should use java.util.LinkedList in such loop
for(true)
if(linkedList.size()>=3){
Person person= new
Person(linkedList.removeFirst(),linkedList.removeFirst(),linkedList.removeFirst());
personList.add(person);
}
else break;
But ArrayList and its get method is good for random access by index which is not your case

If you use java 8 you can try something like this:
public List<Person> createObjectsFromStringList(List<String> list) {
//partition by 3 and list.size.
Map<Integer,List<Integer>> map = IntStream
.range(0,list.size())
.boxed()
.collect(Collectors.groupingBy(e->(e)/3));
List<Person> personList = new ArrayList<>();
map.entrySet().forEach(e->{
List<String> per= e.getValue();
Person p = new Person(per.get(0),per.get(1),per.get(2));
personList.add(p);
});
return personList;
}

Related

Java - Retrive Indivudual Values from a List in a Map

I have a TreeMap with 3 entries, all from individual ArrayLists.
I use the following code:
Map<String, List<String>> mapOne = new TreeMap<String, List<String>>();
List<String> listFour = Arrays.asList("");
ArrayList<String> listOne = new ArrayList<>();
listOne.add("Writer");
listOne.add("Actor");
listOne.add("Politician");
listOne.add("Dancer");
ArrayList<String> listTwo = new ArrayList<>();
listTwo.add("James");
listTwo.add("Robert");
listTwo.add("Tereza");
listTwo.add("John");
ArrayList<String> listThree = new ArrayList<>();
listThree.add("Joyce");
listThree.add("Redford");
listThree.add("May");
listThree.add("Travolta");
for (int i = 0; i < listOne.size(); i++) {
String stringOne = listOne.get(i);
String stringTwo = listTwo.get(i);
String stringThree = listThree.get(i);
listFour = Arrays.asList(stringTwo, stringThree);
mapOne.put(stringOne, listFour);
}
Now I want to obtain the individual String values from the sorted list. like so:
for (Map.Entry<String, List<String>> entry : mapOne.entrySet()) {
String key = entry.getKey();
for (String value : entry.getValue()) {
System.out.println(value);
}
}
The above code prints a list like
{Robert Redford , John Travolta , Tereza May , James Joyce}
Is it possible to iterate over the list in a way as to obtain to separate lists, one with the first names and the other with the last names?
listOne = {Robert , John , Tereza, James}
listTwo = {Redford, Travolta, May, Joyce}
Or should I use an entirely different approach?
The whole thing started out with the need to sort one ArrayList and to other accordingly. What seemed trivial at the beginning, turned out to be a real challenge.
I am a sort of a hobby programmer, so the pros out there kindly bear with me.
It seems you're over-engineering somehow.
I will answer with a Stream solution first, just for the sake of trying it.
Note that I'd, personally, prefer the "old" iterative approach (see below).
// You can see by the use of AtomicInteger that this isn't the right road to take!
final AtomicInteger i = new AtomicInteger();
final Collection<List<String>> values1 =
mapOne.values()
.stream()
.flatMap(v -> v.stream())
.collect(partitioningBy(o -> i.getAndIncrement() % 2 != 0))
.values();
Output: [[Robert, John, Tereza, James], [Redford, Travolta, May, Joyce]]
Iterative approach
final Collection<String> names = new ArrayList<>();
final Collection<String> surnames = new ArrayList<>();
for (final List<String> value : mapOne.values()) {
names.add(value.get(0));
surnames.add(value.get(1));
}
Output:
[Robert, John, Tereza, James]
[Redford, Travolta, May, Joyce]
This is safe because you know each inner List has two elements.
What JB Nizet is telling you to do (credit to him for writing that), is basically to create an appropriate class
public class Person {
private String profession;
private String name;
private String surname;
// Getters and setters. JavaBean style
}
And proceed to sort a Collection<Person>.
For example, keeping it as simple as possible
final List<Person> persons = new ArrayList<>();
// Populate list
Collections.sort(persons, (p1, p2) -> {
// Not null-safe
return p1.getName().compareTo(p2.getName());
});
This will sort the list by name. It will not return a new List, but simply modify the input one.
Putting all the valuable tips, online searches and my efforts together, this is my final solution (in the hope that it might be useful to others). It seems to me my solution is minimal and straightforward :
List<Sites> listOfSites = new ArrayList<Sites>();
for (int i = 0; i < classLists.listSites.size(); i++) {
String site = classLists.listSites.get(i);
String description = classLists.listSitesDesc.get(i);
String link = classLists.listSitesLinks.get(i);
listOfSites.add(new Sites(site, description, link));
}
Collections.sort(listOfSites, (p1, p2) -> {
return p1.getName().compareTo(p2.getName());
});
final ArrayList<String> titles = new ArrayList<>();
final ArrayList<String> descriptions = new ArrayList<>();
final ArrayList<String> links = new ArrayList<>();
for (Sites s : listOfSites) {
String name = s.getName();
String description = s.getDesc();
String link = s.getLink();
titles.add(name);
descriptions.add(description);
links.add(link);
}
Below the class Sites:
public class Sites implements Comparable<Sites> {
private String name;
private String description;
private String link;
public Sites(String name, String description, String link) {
this.name = name;
this.description = description;
this.link = link;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return description;
}
public void setDesc(String description) {
this.description = description;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}

group (gather) items of an object

i have a List of an Object, with the following characteristics:
Class Object{
String gender;
String state;
int quantity;
int Salary;
}
List<Object> myList=new ArrayList<Object>;
As input of the List, i have the following:
and as Output, i want to keep only one occurrence of the object with the same gender and the same state, in the same time sum the quantity and the salsary correspanding, like the following:
my question is how can i loop through myList, find objects with the same gender and the same state,keep only one occurence of them, and sum the quantity and the salary correspanding ??
First off, I renamed your class to MyObject as Object is the base Java class. Now for the rest of it - You can use a pseudo-index made out of the gender and state combinations you have already found and sum up the values for the rest of the list as follows:
Class MyObject{
String gender;
String state;
int quantity;
int Salary;
}
List<MyObject> myList=new ArrayList<MyObject>();
List<String> stateAndGender = new ArrayList<String>();
List<MyObject> finalList = new ArrayList<MyObject>();
// add objects here
for(MyObject mO : myList){
String s = mO.getGender();
s+="," + mO.getState();
if(stateAndGender.indexOf(s)==-1)
{
MyObject fO = new MyObject();
fO.setGender(mO.getGender());
fO.setState(mO.getState());
stateAndGender.add(s);
int Qua = mO.getQuantity();
int Sal = mO.getSalary();
for(int i=0; i<myList.size(); i++)
{
if(String t = myList.get(i).getGender()+","+myList.get(i).getGender() == s)
Qua += myList.get(i).getQuantity();
Sal += myList.get(i).getSalary();
}
fO.setQuantity(Qua);
fO.setSalary(Sal);
finalList.add(fO);
}
}
// Then return finalList
The above code assumes you have proper getters for the fields of the class you have created and your gender and state do not contain commas, otherwise you will need to tweak the code.
UPDATE: Now you get a list of the MyObject type with the proper values as you requested! It is not the original one, though, but the one called finalList.

Best way to filter out collections based on certain rules

I am trying to find a best way to filter out some items from a list based on certain rules. For example we have
public class Person{
String name;
String sex;
String dob;
String contactNo;
Person(String name, String sex, String dob, String contactNo) {
this.name = name;
this.sex = sex;
this.dob = dob;
this.contactNo = contactNo;
}
}
List<Person> persons = Arrays.asList(new Person("Bob", "male", "19800101", "12345"),
new Person("John", "male", "19810101", "12345"),
new Person("Tom", "male", "19820101", "12345"),
new Person("Helen", "female", "19800101", "12345"),
new Person("Jack", "male", "19830101", "12345"),
new Person("Suan", "female", "19850101", "12345"));
I want to remove the pair of male and female which have the same dob and contactNo (Remove Bob and Helen in above example). I implemented this as below using a nested loop which worked but looks ugly. Is there a better to achieve this please? Can I implement predicate to do this?
public void filterPersons() {
List<Person> filtered = new ArrayList<Person>();
for (Person p: persons) {
boolean pairFound = false;
for (Person t: persons) {
if ((p.sex.equals("male") && t.sex.equals("female")) || (p.sex.equals("female") && t.sex.equals("male"))) {
if (p.dob.equals(t.dob) && p.contactNo.equals(t.contactNo)) {
pairFound = true;
break;
}
}
}
if (!pairFound) {filtered.add(p);}
}
System.out.println("filtered size is: " + filtered.size());
for (Person p: filtered) {
System.out.println(p.name);
}
}
Many thanks.
I've rewritten the above method something like below which looks better imho:
public void testFilter() {
Predicate<Person> isPairFound = new Predicate<Person>() {
#Override public boolean apply(Person p) {
boolean pairFound = false;
for (Person t: persons) {
if ((p.sex.equals("male") && t.sex.equals("female")) ||
(p.sex.equals("female") && t.sex.equals("male"))) {
if (p.dob.equals(t.dob) && p.contactNo.equals(t.contactNo)) {
pairFound = true;
break;
}
}
}
return pairFound;
}
};
Iterable<Person> filtered = Iterables.filter(persons, isPairFound);
for (Person p: filtered) {
System.out.println(p.name);
}
}
I don't think the nested for loops are particularly ugly. You are looking for matches between items in your list based on, effectively, arbitrary criteria so you need to compare every entry with every other entry.
One improvement you could consider is to separate the iterating code from the comparison logic. This is where you were heading with the Predicate. To do this you would need a Predicate that takes two objects instead of one.
public interface PredicateComparator<T> {
boolean compare(T o1, T o2);
}
Your code would now look something like this
public void filterPersons() {
PredicateComparator<Person> predicate = new PredicateComparator<Person>() {
public boolean compare(Person o1, Person o2) {
// comparison logic in here
}
};
List<Person> filtered = new ArrayList<Person>();
for (Person p : persons) {
for (Person t : persons) {
if (predicate.compare(p, t)) {
filtered.add(p);
}
}
}
System.out.println("filtered size is: " + filtered.size());
for (Person p: filtered) {
System.out.println(p.name);
}
}
You can use hashmap to remove duplicates. Each entry in the map would denote
(DOB+ContactNo -> persons index in the original list)
The function
public void filterPersons() {
List<Person> filtered = new ArrayList<Person>(persons); // COPY the whole list
HashMap<String,Integer> map = new HashMap<String,Integer>();
int count=-1;
for (Person p: persons) {
count++;
String g = p.sex;
String g_opp = g.equals("male")? "female":"male";
if(!map.contains(p.dob+p.contactNo+g_opp))
{
// if not exists, add to map
map.put(p.dob+p.contactNo,count+g);
}
else
{
// if duplicate found in map, remove both people from list
filtered.remove(count);
filtered.remove(map.get(p.dob+p.contactNo+g));
// now filtered has 2 less elements, update count
count -= 2;
}
}
}
Is there only one way to determine identity between two persons?
If so, it would be best to encapsulate this by overriding 'equals' and 'hashcode'.
After you do this you can take one of the following approaches:
If you are creating a collection of Person instances and want to make sure that when adding the same person more than once will preserver only a single instance in the collection- use the Set interface as the underlying collection (and probably the HashSet implementation). With equals and hashcode properly in place, the set will not allow duplicates.
If you are given a collection (meaning you have no control of it's creation and therefore can't use the above approach to validate that it is constructed without duplicates) and want to filter out duplicate instances you can simply feed it to the constructor of a HashSet, like this:
Collection<Integer> containsRepeatingNumbers = Arrays.asList(1,2,3,4,3,3,3,3);
Set<Integer> alldistincts = new HashSet<>(containsRepeatingNumbers);
System.out.println(alldistincts); //[1, 2, 3, 4]
BTW, if you anticipate multiple criteria for identity in the future, you can use the strategy propsed here

Compare 2 Java arraylists of different objects and add the matching rows to a new List

We need to compare 2 arraylists of different objects having some common fields, and then store the matching rows to a new arraylist. I have searched for solutions, but wasn't able to get what I need.
List<Person> personList = new ArrayList<Person>();
Person:
private String firstName;
private String lastName;
private String street1;
private String street2;
private String city;
private String stateCode;
private String zipCode;
List<PersonNpi> npiList = new ArrayList<PersonNpi>();
PersonNpi:
private String name;
private String npi;
private Address address;
So I need to check if the name & address in the PersonNpi object in the PersonNpiList match to a Person object in the PersonList, and if yes save the Person details + Npi to a new Arraylist<Employee>
Hope I'm clear on the question. Please let me know on how to solve this efficiently.
Thanks
Harry
EDIT:
I need to save the non-matching rows (on the first arraylist) as well to another list. Do I need to have another loop or can I do it on the same For loop? Anyone please?
Since I don't see any superclasses from which they extend, you have to manually iterate through your lists. I am assuming a lot, for instance that you have getters and setters for your attributes, that PersonNpi.name is more or less the same as Person.firstname + Person.lastname, that you have some function in Address like boolean checkEquality(String street1, String street2, String city, String state, String zip), that your Person class has a getName() method to compare with PersonNpis. In that case, loop through the first array, and check for every item if the second has anything equal to it.
ArrayList<Employee> employees = new ArrayList<Employee>();
for(Person person : personList) {
for(PersonNpi personNpi : npiList) {
if (person.getName().equals(personNpi.getName()) &&
person.getAddress().checkEquality(...address parts here...)) {
employees.add(new Employee(person, personNpi));
}
}
}
Again, I made a lot of assumptions, also the one that you have an Employee constructor which just requires the Person and the PersonNpi, and gets the required information accordingly.
You should elaborate more, use superclasses, and use the contains() function. In other words, make comparing the Person and the PersonNpi easier through a function.
Edit: your second question is highly, if not extremely dependant on your further implementation of Employee, Person and PersonNpi. For now, I'll yet again assume you have some methods that verify equality between Employee, Person and PersonNpi.
I'd suggest to not do the checking in one loop, since you have two ArrayLists which are ran through. The PersonNpi-list is ran through for every record in the first List. So what might happen is after we checked everything, a few Persons are left unmatched, and a few PersonNpis are left unmatched, since we don't flag which Persons and PersonNpis we've matched.
In conclusion: for easiness' sake, just add this part:
ArrayList<Object> nonMatchedPersons = new ArrayList<Object>();
for (Person person : personList)
if (!employees.contains(person))
nonMatchedPersons.add(person);
for (PersonNpi personNpi : npiList)
if (!employees.contains(personNpi))
nonMatchedPersons.add(personNpi);
This method does require you to implement the equals(Object) method for all 3 person classes, which you might consider putting beneath a superclass like Human. In that case, you can make the Object ArrayList into a ArrayList<Human>
With one loop (requires equals(Object) method for the 3 person classes):
List<Employee> employees = new ArrayList<Employee>();
ArrayList<Object> nonMatchedPersons = new ArrayList<Object>();
Iterator<Person> personIterator = personList.iterator();
while (personIterator.hasNext()) {
Iterator<PersonNpi> npiIterator = npiList.iterator();
while(npiIterator.hasNext()) {
Person person = personIterator.next();
PersonNpi personNpi = npiIterator.next();
if (person.equals(personNpi)) {
employees.add(new Employee(person, personNpi));
personIterator.remove();
npiIterator.remove();
}
}
}
nonMatchedPersons.addAll(personList);
nonMatchedPersons.addAll(npiList);
Explanation: we loop with Iterators through both lists, to enable us to remove from the list while iterating. So in the personList and the npiList, only the singles remain, as we add doubles to the Employee-list, instantly removing them from the other two lists. We add the remaining singles in the two lists to our nonMatchedPerson-list with the addAll method.
Edit2: If you can't edit those classes for whatever reason, make 3 wrapper classes, something like:
public class PersonWrapper {
private Person person;
public PersonWrapper(Person person) {
this.person = person;
}
#override
public boolean equals(Object other) {
if (other == null)
return false;
if (other instanceof PersonWrapper) {
//etc etc, check for equality with other wrappers.
...
}
}
}
If you choose to use this approach, change this line in the loop:
if (person.equals(personNpi)) {
to this:
if (new PersonWrapper(person).equals(new PersonNpiWrapper(personNpi))) {
Using this, you can still implement your own equals() method.
Another solution could be that you make a static method like this:
public static boolean equals(Object this, Object that) {
if (this instanceof Person || this instanceof PersonNpi) //et cetera, et cetera
return true;
return false;
}
Now just call Person.equals(person, personNpi), assuming you put the method in the class Person.
If you implement equals to compare the values under question, you can then use contains to see if object is in other list.
Otherwise you'll have to manually iterate though lists, and check each object.
And if you using jdk8 Lambda, you could do something like this (compiles and runs btw, with correct jdk) :
public static void main(String args[]) throws ParseException {
TransformService transformService = (inputs1, inputs2) -> {
Collection<String> results = new ArrayList<>();
for (String str : inputs1) {
if (inputs2.contains(str)) {
results.add(str);
}
}
return results;
};
Collection<String> inputs1 = new ArrayList<String>(3) {{
add("lemon");
add("cheese");
add("orange");
}};
Collection<String> inputs2 = new
ArrayList<String>(3) {{
add("apple");
add("random");
add("cheese");
}};
Collection<String> results = transformService.transform(inputs1, inputs2);
for (String result : results) {
System.out.println(result);
}
}
public interface TransformService {
Collection<String> transform(Collection<String> inputs1, Collection<String> inputs2);
}
Something like this should work. It assumes that you have a way of constructing an Employee from a Person and a PersonNpi. Also, since you don't tell the structure of an Address, I'll leave it to you to write the address matching logic.
public List<Employee> findCommonElements(List<Person> list1,
List<PersonNpi> list2)
{
List<Employee> common = new ArrayList<Employee>();
for (Person p1 : list1) {
PersonNpi p2 = find(list2, p1);
if (p2 != null) {
common.add(new Employee(p1, p2));
}
}
}
private PersonNpi find(List<PersonNpi> list, Person p) {
for (PersonNpi p2 : list) {
if (matches(p, p2)) {
return p2;
}
}
return null;
}
private boolean matches(Person p1, PersonNpi p2) {
return /* logic for comparing name and address info */;
}
This is an O(n2) operation. You could speed this up considerably by sorting both arrays by name and address. The sorting operation is O(n log(n)) and the comparison could then be implemented as an O(n) operation.
Use HashMap to store the first list PersonNpiList. Use map.get(Person) == null to check whether the person is in the hash map.

not sure if I should search or sort my hashmap

Hi I have a list of people with their ages, I need to find those who are more than 30 years old,
is there any possibility to search in a hashmap ? (please note that I may need to look for those in other age ranges as well so I prefer not to use two different lists for the sake of simplicity of code)
In short: My goal is to find a way to search for elements with specific values in HashMap
Sample list is
element1 40
element2 4
element3 66
element4 5
I want to find those with values more than 40 and those with values more than or equal to 66.
I'd suggest you to use NavigableMap (Implemented as TreeSet).
This implementation is a quite fast - O(log(N)), versus O(N) if you implement index based on lists.
Edit. Example:
class PersonsAgeIndex {
private NavigableMap<Integer, List<Person>> ageToPersons =
new TreeMap<Integer, List<Person>>();
public void addPerson( Person p ) {
List<Person> personsWithSameAge = this.ageToPersons.get( p.age );
if ( personsWithSameAge == null ) {
personsWithSameAge = new LinkedList<Person>();
this.ageToPersons.put( p.age, personsWithSameAge );
}
personsWithSameAge.add( p );
}
public List<Person> personsWithAgeLessThan( int age ) {
List<Person> persons = new LinkedList<Person>();
// persons with less age
for (List<Person> tmp : this.ageToPersons.headMap( age ).values()) {
persons.addAll( tmp );
}
return persons;
}
public List<Person> personsWithAgeInInterval( int minAge, int maxAge ) {
List<Person> persons = new LinkedList<Person>();
// persons with age, which: (minAge <= age <= maxAge)
for (List<Person> tmp : this.ageToPersons.subMap( minAge, true, maxAge, true ).values()) {
persons.addAll( tmp );
}
return persons;
}
}
class Person {
public final int age;
public Person(int age) {
this.age = age;
}
}
Try this:
private List<Person> getPeople(Map<?, Person> peopleMap, int filterAge) {
List<Person> returnList = new ArrayList<Person>(peopleMap.values().size());
for (Person p : peopleMap.values()) {
if (p.getAge() > filterAge)
returnList.add(p);
}
return returnList;
}
HashMapiteration order is 'not predictable' (that's to say that if you sort, and than insert keys, in a determinate order when you later try to iterate the keys the order is not the same).
Use a LinkedHashMap instead.
Good question... unfortunately, a Map needs a very specific key. Your solution above is the only real way to do it.
Alternatively you could maintain two lists, and store those that older than 30 to the 2nd list.
You cannot sort a HashMap, it has no order. If you want an ordered HashMap, use LinkedHashMap.
HashMap<String,String> hmap = new HashMap<String,String>();
SortedSet<String> keys = new TreeSet<String>(hmap.keySet());
This will give you a sorted set which you could make a subset of.
keys.subSet(from,to) e.g keys.subSet(30,100)
and you will have a set with all required elemets.

Categories