4

I have data composed of a list of employers and a list of workers. Each has a many-to-many relationship with the other (so an employer can have many workers, and a worker can have many employers).

The way the data is retrieved (and given to me) is as follows: each employer has an array of workers. In other words:

employer n has:
  worker x, worker y etc.

So I have a bunch of employer objects each containing an array of workers.

I need to transform this data (and basically invert the relationship). I need to have a bunch of worker objects, each containing an array of employers. In other words:

worker x has:
  employer n1, employer n2 etc.

The context is hypothetical so please don't comment on why I need this or why I am doing it this way. I would really just like help on the algorithm to perform this transformation (there isn't that much data so I would prefer readability over complex optimizations which reduce complexity). (Oh and I am using Java, but pseudocode would be fine).

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
tkm
  • 43
  • 3

2 Answers2

4

You want a Map<Worker,Set<Employer>> for this, or possibily a Multimap<Worker,Employer> from Guava.

In pseudocode:

initialize employerMap(worker => set of employers)
for every employer n do
    for every worker x in n.workers do
         employerMap[x].add(n)

Essentially a Worker can be mapped to multiple Employer, so you either:

  • Have a Map<Worker,Set<Employer>>
    • each key in a Map can only have one value, so the value in this case is a Set of values
  • Have a Multimap<Worker,Employer>
    • Multimap can map a key to multiple values

Example

Here's an example of doing the mapping. Note that storing the data in Object[][] arr like this is definitely not recommended, and is only used as part of the example. Concentrate on the actual mapping part.

import java.util.*;
public class MultiMapExample {
    public static void main(String[] args) {
        Object[][] arr = {
            { "IBM",    new String[] { "Joe", "Jack", "Carol"   }},
            { "MS",     new String[] { "Jack", "Andy", "Carol" }},
            { "Google", new String[] { "Bob", "Alice", "Carol"  }},
        };
        Map<String,Set<String>> employerMap =
            new HashMap<String,Set<String>>();

        for (Object[] data : arr) {
            String employer = (String) data[0];
            String[] workers = (String[]) data[1];
            for (String worker : workers) {
                Set<String> employers = employerMap.get(worker);
                if (employers == null) {
                    employerMap.put(worker, employers = new HashSet<String>());
                }
                employers.add(employer);
            }
        }

        for (String worker : employerMap.keySet()) {
            System.out.println(worker + " works for " + employerMap.get(worker));
        }
    }
}

This prints (order may vary):

Alice works for [Google]
Jack works for [IBM, MS]
Bob works for [Google]
Andy works for [MS]
Carol works for [IBM, Google, MS]
Joe works for [IBM]

I recommend keeping the data in a Map like this, but if you must transform the list of employees to an array for some reason, you can use Collection.toArray(T[]).

You should generally prefer List and other Java Collections Framework classes to arrays, though.


On implementing equals, hashCode, etc

The above example uses String for simplicity. You probably should have an actual Worker and Employer types instead, which is a good thing. You have to make sure that they implement equals and hashCode properly, though.

See also

  • Effective Java 2nd Edition
    • Item 8: Obey the general contract when overriding equals
    • Item 9: Always override hashcode when you override equals

API links

Related questions

On equals/hashCode combo:

On equals vs ==:

Community
  • 1
  • 1
polygenelubricants
  • 348,637
  • 121
  • 546
  • 611
3

Not java-specific, but here's the approach:

  • make a dictionary of <Worker, Set<Employer>>,
  • iterate through all the workers of each employer, and
  • for every worker, add the current employer to the Dict[Worker] set

You'll need to add a check to create the set the first time each worker is found.

tzaman
  • 42,181
  • 9
  • 84
  • 108
  • I understand bullet 1, but how to I do bullet 2 given the dictionary made in the bullet 1? – tkm Jun 10 '10 at 14:24
  • Make a function that takes your list of employer objects, creates the dictionary, and walks the employer list, adding each worker to the dictionary. – tzaman Jun 10 '10 at 14:26
  • sorry if this is obnoxious, but could I please have pseudocode, I can't seem to wrap my head around it? – tkm Jun 10 '10 at 14:29