简体   繁体   中英

Convert List of pairs into list of grouped pairs

I could not find out a good thread name for this problem :CI have list of objects that have two fields with long values:

class ObjectFromDb {
    Long dbUserId;
    Long apiUserId;
}

Eg. it can look like this:

List<ObjectFromDb> dbList = {
    {1, 1},
    {2, 2},
    {2, 3},
    {3, 4},
    {4, 4},
    {5, 4},
    {6, 5}
}

The goal is to group this list to relations. Possibilities are: One to one, one to many, many to one. Not many to many allowed.

My final object:

class FinalObject {
    List<Long> dbIds;
    List<Long> apiIds;
}

So the result list should be:

List<FinalObject> finalList = {
    { {1}    , {1}   },
    { {2}    , {2,3} },
    { {3,4,5}, {4}   },
    { {6}    , {5}   }
}

And here is my question. Is there any algorithm to solve problem like this? Or if anyone has an idea how to approach it? I was trying to solve it but it ends up with billion ifs and for loops,so I started again and end up with another billion loops..

Here is the idea for a solution:

Step 1 (many to one relations): Sort dbList according to dbUserID , combine apiUserIds with the same dbUserID and remove the corresponding relations from the dbList (in the code I built a new list called updatedDbList1 ).

Step 2 (one to many relations): Sort dbList according to apiUserId , combine dbUserIDs with the same apiUserIds and remove the corresponding relations from the dbList (in the code I built a new list called updatedDbList2 ).

Step 3 (one to one relations): Add the remaining relations to the final result.

This is what I came up with, though I am sure it can be done more clean with a little more thought.

List<FinalObject> finalList = new ArrayList<>();

//Step 1: Extract many to one relations
//assuming dbList is sorted according to dbUserId, if not perform dbList.sort() with custom comparator
List<ObjectFromDb> updatedDbList1 = new ArrayList<>();
ArrayList<Long> apiUserIDs = new ArrayList<Long>();
Long currentdbUserID = -1L;
for(int i=0; i < dbList.size(); i++) {
    if(dbList.get(i).dbUserId != currentdbUserID) {
        if(apiUserIDs.size() >= 2) {
            ArrayList<Long> dbUserIDs = new ArrayList<Long>();
            dbUserIDs.add(currentdbUserID);
            finalList.add(new FinalObject(dbUserIDs, apiUserIDs));
        }
        if(i==dbList.size()-1 || dbList.get(i).dbUserId != dbList.get(i+1).dbUserId){
            updatedDbList1.add(dbList.get(i));
        }
        currentdbUserID = dbList.get(i).dbUserId;
        apiUserIDs = new ArrayList<Long>();
    }
    apiUserIDs.add(dbList.get(i).apiUserId);
}

//Step 2: Extract one to many relations
//assuming dbList is sorted according to apiUserId, if not perform dbList.sort() with custom comparator
List<ObjectFromDb> updatedDbList2 = new ArrayList<>();
ArrayList<Long> dbUserIDs = new ArrayList<Long>();
Long currentApiUserID = -1L;
for(int i=0; i < updatedDbList1.size(); i++) {
    if(updatedDbList1.get(i).apiUserId != currentApiUserID) {
        if(dbUserIDs.size() >= 2) {
            apiUserIDs = new ArrayList<Long>();
            apiUserIDs.add(currentApiUserID);
            finalList.add(new FinalObject(dbUserIDs, apiUserIDs));
        }
        if(i==updatedDbList1.size()-1 || updatedDbList1.get(i).apiUserId != updatedDbList1.get(i+1).apiUserId){
            updatedDbList2.add(updatedDbList1.get(i));
        }
        currentApiUserID = updatedDbList1.get(i).apiUserId;
        dbUserIDs = new ArrayList<Long>();
    }
    dbUserIDs.add(updatedDbList1.get(i).dbUserId);
}

//Step 3: Extract one to one relations
for(int i=0; i < updatedDbList2.size(); i++) {
    dbUserIDs = new ArrayList<Long>();
    dbUserIDs.add(updatedDbList2.get(i).dbUserId);
    apiUserIDs = new ArrayList<Long>();
    apiUserIDs.add(updatedDbList2.get(i).apiUserId);
    finalList.add(new FinalObject(dbUserIDs, apiUserIDs));
}

I am not sure your algorithm for the relationships that covers all possibilites, but here is the code which provides the output you want.

I explained what it does and how it works with comments.

  // First it is gonna group dbUserIds and apiUserIds between each other. To keep this data, we are gonna use hashMaps
        Map<Long, List<Long>> dbUserIdGroup = new HashMap<Long, List<Long>>();
        Map<Long, List<Long>> apiUserIdGroup = new HashMap<Long, List<Long>>();

        // To demonstrate the test data given by you
        List<ObjectFromDb> dbList = new ArrayList<ObjectFromDb>();
        dbList.add(new ObjectFromDb(1L, 1L));
        dbList.add(new ObjectFromDb(2L, 2L));
        dbList.add(new ObjectFromDb(2L, 3L));
        dbList.add(new ObjectFromDb(3L, 4L));
        dbList.add(new ObjectFromDb(4L, 4L));
        dbList.add(new ObjectFromDb(5L, 4L));
        dbList.add(new ObjectFromDb(6L, 5L));

        // Iterating the given ObjectFromDb instances to group them 
        for (ObjectFromDb objectFromDb : dbList) {

            // Grouping according to dbUserId
            if (dbUserIdGroup.get(objectFromDb.getDbUserId()) == null) {
                List<Long> group = new ArrayList<Long>();
                group.add(objectFromDb.getApiUserId());
                dbUserIdGroup.put(objectFromDb.getDbUserId(), group);
            } else {
                dbUserIdGroup.get(objectFromDb.getDbUserId()).add(objectFromDb.getApiUserId());
            }

            // Grouping according to apiUserId
            if (apiUserIdGroup.get(objectFromDb.getApiUserId()) == null) {
                List<Long> group = new ArrayList<Long>();
                group.add(objectFromDb.getDbUserId());
                apiUserIdGroup.put(objectFromDb.getApiUserId(), group);
            } else {
                apiUserIdGroup.get(objectFromDb.getApiUserId()).add(objectFromDb.getDbUserId());
            }
        }

        // Up to now, we have two grouped hashmaps 
        // dbUserIdGroup ->  {"1":[1],"2":[2,3],"3":[4],"4":[4],"5":[4],"6":[5]} // note that the key of this hashtable is dbUserId
        // apiUserIdGroup ->  {"1":[1],"2":[2],"3":[2],"4":[3,4,5],"5":[6]} //  note that the key of this hashtable is apiUserId
        Set<Long> dbUserIds = dbUserIdGroup.keySet(); // to iterate dbUserId group we get its keys (dbUserIds)

        Set<List<Long>> existanceCheck = new HashSet<>(); // to avoid duplicated data

        List<FinalObject> result = new ArrayList<FinalObject>(); // to keep the result
        for (Long dbUserId : dbUserIds) {
            FinalObject fObject = null;
            List<Long> dbApiIdList = dbUserIdGroup.get(dbUserId);

            if (dbApiIdList.size() == 1) { // if the value is the list with single element
                List<Long> groupedDbUserId = apiUserIdGroup.get(dbApiIdList.get(0));
                if (!existanceCheck.contains(groupedDbUserId)) {
                    fObject = new FinalObject(groupedDbUserId, dbApiIdList);
                    existanceCheck.add(groupedDbUserId);
                    result.add(fObject);
                }
            } else { // if the value is the list with multiple elements
                List<Long> dbUserIdList = new ArrayList<Long>();
                dbUserIdList.add(dbUserId);
                fObject = new FinalObject(dbUserIdList, dbApiIdList);
                result.add(fObject);
            }

        }

        // Now you have a List<FinalObject> result array just like you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM