1

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?

Harry
  • 546
  • 6
  • 22
  • 50

4 Answers4

4

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.

IPV3001
  • 272
  • 3
  • 16
stealthjong
  • 10,858
  • 13
  • 45
  • 84
  • Thanks so much for the code. This is similar to what I want & what I was building. I was looking for other ideas. and sorry for not elaborating on the objects :( – Harry Dec 04 '12 at 16:08
  • I get OutOfMemeory errors. I'm now trying to sort the lists before comparing. Is there any way to use contains instead as Nicolay suggested? – Harry Dec 05 '12 at 15:23
  • @Harish It would be easier if I had some more context. Some more code would be nice. – stealthjong Dec 05 '12 at 15:30
  • Sorry for the late reply. I have another q Christiaan. I have almost similar code, is there a way to get the non matching rows from the same loop or do I have to have another loop to get the non matching ones? – Harry Dec 18 '12 at 16:42
  • @Harish See my edit, in which I elaborated on your secondary question. – stealthjong Dec 18 '12 at 23:55
  • Thanks so much for your elaborate explanation. I think I did not add this comment earlier to your answer "The Objects are generated from xsd using maven & not possible to override equals method". So I'm not sure if the edit would work? – Harry Dec 19 '12 at 15:56
  • @Harish It's actually quite easy and there are many roads that lead to Rome. See my addition for two roads. – stealthjong Dec 19 '12 at 22:28
2

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);
    }
NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
2

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.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
-2

Use HashMap to store the first list PersonNpiList. Use map.get(Person) == null to check whether the person is in the hash map.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
David Ruan
  • 534
  • 4
  • 18