Open In App

Merge details of students (Merging Details)

Last Updated : 05 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Bob, a teacher of St. Joseph School given a task by his principal to merge the details of the students where each element details[i] is a list of strings, where the first element details[i][0] is a name of the student, and the rest of the elements are emails representing emails of the student. Two details belong to the same student if there is some common email to both details. After merging the details, return the details of the student in the following format: the first element of each detail is the name of the student, and the rest of the elements are emails in sorted order.

Note: Two details have the same name, they may belong to different people as people could have the same name. A person can have any number of details initially, but all of their details have the same name.

In case 2 or more same email belongs to 2 or more different names merge with first name only. Return the 2D list in the order in a sorted way according to the name of the details.

Example:

Input: n: 4

details = [[“John”,”johnsmith@mail.com”,”john_newyork@mail.com”], [“John”,”johnsmith@mail.com”,”john00@mail.com”], [“Mary”,”mary@mail.com”], [“John”,”johnnybravo@mail.com”]]

Output: [[“John”,”john00@mail.com”,”john_newyork@mail.com”, “johnsmith@mail.com”],[“Mary”,”mary@mail.com”], [“John”,”johnnybravo@mail.com”]]

Explanation: The first and second John’s are the same person as they have the common email “johnsmith@mail.com”. The third John and Mary are different people as none of their email addresses are used by other accounts. We could return these lists in any order, for example the answer [[‘Mary’, ‘mary@mail.com’], [‘John’, ‘johnnybravo@mail.com’], [‘John’, ‘john00@mail.com’, ‘john_newyork@mail.com’, ‘johnsmith@mail.com’]] would still be accepted.

Input: n: 5,
details = [[“Gabe”,”Gabe0@m.co”,”Gabe3@m.co”,”Gabe1@m.co”],[“Kevin”,”Kevin3@m.co”,”Kevin5@m.co”,”Kevin0@m.co”],[“Ethan”,”Ethan5@m.co”,”Ethan4@m.co”,”Ethan0@m.co”],[“Hanzo”,”Hanzo3@m.co”,”Hanzo1@m.co”,”Hanzo0@m.co”],[“Fern”,”Fern5@m.co”,”Fern1@m.co”,”Fern0@m.co”]]

Output: [[“Ethan”,”Ethan0@m.co”,”Ethan4@m.co”,”Ethan5@m.co”],[“Gabe”,”Gabe0@m.co”,”Gabe1@m.co”,”Gabe3@m.co”],[“Hanzo”,”Hanzo0@m.co”,”Hanzo1@m.co”,”Hanzo3@m.co”],[“Kevin”,”Kevin0@m.co”,”Kevin3@m.co”,”Kevin5@m.co”],[“Fern”,”Fern0@m.co”,”Fern1@m.co”,”Fern5@m.co”]]

Explanation: We don’t have any common emails in any of the users. We just sorted the emails of each person and we return a list of the emails.(The details can be returned in any order).

Approach:

The problem involves merging connected components (emails), making Disjoint Set Union (DSU) a natural choice for solving it. We use integer IDs to represent each connected component.. Initially, each account is mapped to a unique ID. We create a dictionary mapping an account index to itself. Iterating over emails in all accounts, we build a dictionary mapping email to the account index. If the same email appears multiple times, we perform a union operation to unite corresponding subsets under the same account index. Subsequently, we construct a dictionary mapping a merged account to its emails, utilizing the find operation to determine the final account index and appending associated emails. Finally, we compile the resulting list.

Steps-by-step approach:

  • Create find() for root of union-find.
  • Create mergeDetails() for account merging.
  • Setup Union-Find and Mapping:
    • Use m for email to account mapping.
    • Create union_find for disjoint sets.
    • Use res_map for merged accounts and emails.
  • Iterate Over Accounts:
    • Loop through accounts.
    • Find union find index.
    • Merge accounts with common emails.
  • Map root indices to merged emails using res_map.
  • Sort emails and insert account name.
  • Compile final result vector.

Below is the implementation of the above approach:

C++




#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <vector>
 
using namespace std;
 
// Function to find the root of the union find array
int find(vector<int>& union_find, int ind)
{
    while (union_find[ind] != ind)
        ind = union_find[ind];
    return ind;
}
 
// Function to merge the accounts
vector<vector<string> >
mergeDetails(vector<vector<string> >& accounts)
{
    unordered_map<string, int> m;
    vector<int> union_find(accounts.size(), 0);
    unordered_map<int, vector<string> > res_map;
 
    // Initializing union find array and mapping email to
    // account index
    for (int i = 0; i < accounts.size(); i++) {
        union_find[i] = i;
        for (int j = 1; j < accounts[i].size(); j++) {
            if (m.find(accounts[i][j]) != m.end()) {
                int root1 = find(union_find, i);
                int root2
                    = find(union_find, m[accounts[i][j]]);
                union_find[root1] = root2;
            }
            else
                m[accounts[i][j]] = union_find[i];
        }
    }
 
    // Mapping root index to merged emails
    for (auto it : m) {
        int ind = find(union_find, it.second);
        res_map[ind].push_back(it.first);
    }
 
    // Constructing final result with sorted emails
    vector<vector<string> > res;
    for (auto it : res_map) {
        vector<string> email = it.second;
        sort(email.begin(), email.end());
        email.insert(email.begin(), accounts[it.first][0]);
        res.push_back(email);
    }
 
    return res;
}
 
int main()
{
    // Provided Input
    int n = 4;
    vector<vector<string> > details
        = { { "John", "johnsmith@mail.com",
            "john_newyork@mail.com" },
            { "John", "johnsmith@mail.com",
            "john00@mail.com" },
            { "Mary", "mary@mail.com" },
            { "John", "johnnybravo@mail.com" } };
 
    // Call the mergeDetails function and print the result
    vector<vector<string> > result = mergeDetails(details);
    for (const auto& detail : result) {
        for (const string& item : detail) {
            cout << item << " ";
        }
        cout << endl;
    }
 
    return 0;
}


Java




// Java program for the above approach
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
class GFG {
    // Function to find the root of the union find array
    static int find(List<Integer> unionFind, int ind)
    {
        while (unionFind.get(ind) != ind)
            ind = unionFind.get(ind);
        return ind;
    }
         
    // Function to merge the accounts
    static List<List<String> >
    mergeDetails(List<List<String> > accounts)
    {
        Map<String, Integer> emailToAccountIndex
            = new HashMap<>();
        List<Integer> unionFind
            = new ArrayList<>(accounts.size());
        Map<Integer, List<String> > resultMap
            = new HashMap<>();
             
        // Initializing union find array and mapping email
        // to account index
        for (int i = 0; i < accounts.size(); i++) {
            unionFind.add(i);
            for (int j = 1; j < accounts.get(i).size();
                 j++) {
                if (emailToAccountIndex.containsKey(
                        accounts.get(i).get(j))) {
                    int root1 = find(unionFind, i);
                    int root2
                        = find(unionFind,
                               emailToAccountIndex.get(
                                   accounts.get(i).get(j)));
                    unionFind.set(root1, root2);
                }
                else
                    emailToAccountIndex.put(
                        accounts.get(i).get(j),
                        unionFind.get(i));
            }
        }
             
        // Mapping root index to merged emails
        for (Map.Entry<String, Integer> entry :
             emailToAccountIndex.entrySet()) {
            int ind = find(unionFind, entry.getValue());
            resultMap
                .computeIfAbsent(ind,
                                 k -> new ArrayList<>())
                .add(entry.getKey());
        }
             
        // Constructing final result with sorted emails
        List<List<String> > result = new ArrayList<>();
        for (Map.Entry<Integer, List<String> > entry :
             resultMap.entrySet()) {
            List<String> email = entry.getValue();
            email.sort(String::compareTo);
            email.add(0,
                      accounts.get(entry.getKey()).get(0));
            result.add(email);
        }
         
        return result;
    }
 
    public static void main(String[] args)
    {
        // Provided Input
        int n = 4;
        List<List<String> > details = Arrays.asList(
            Arrays.asList("John", "johnsmith@mail.com",
                          "john_newyork@mail.com"),
            Arrays.asList("John", "johnsmith@mail.com",
                          "john00@mail.com"),
            Arrays.asList("Mary", "mary@mail.com"),
            Arrays.asList("John", "johnnybravo@mail.com"));
 
        // Call the mergeDetails function and print the
        // result
        List<List<String> > result = mergeDetails(details);
        for (List<String> detail : result) {
            for (String item : detail) {
                System.out.print(item + " ");
            }
            System.out.println();
        }
    }
}
 
// This code is contributed by Susobhan Akhuli


C#




// C# program for the above approach
using System;
using System.Collections.Generic;
using System.Linq;
 
public class GFG {
    // Function to find the root of the union find array
    static int Find(List<int> unionFind, int ind)
    {
        while (unionFind[ind] != ind)
            ind = unionFind[ind];
        return ind;
    }
 
    // Function to merge the accounts
    static List<List<string> >
    MergeDetails(List<List<string> > accounts)
    {
        Dictionary<string, int> emailToAccountIndex
            = new Dictionary<string, int>();
        List<int> unionFind
            = Enumerable.Range(0, accounts.Count).ToList();
        Dictionary<int, List<string> > resultMap
            = new Dictionary<int, List<string> >();
 
        // Initializing union find array and mapping email
        // to account index
        for (int i = 0; i < accounts.Count; i++) {
            for (int j = 1; j < accounts[i].Count; j++) {
                if (emailToAccountIndex.ContainsKey(
                        accounts[i][j])) {
                    int root1 = Find(unionFind, i);
                    int root2 = Find(
                        unionFind,
                        emailToAccountIndex[accounts[i]
                                                    [j]]);
                    unionFind[root1] = root2;
                }
                else
                    emailToAccountIndex[accounts[i][j]]
                        = unionFind[i];
            }
        }
 
        // Mapping root index to merged emails
        foreach(var entry in emailToAccountIndex)
        {
            int ind = Find(unionFind, entry.Value);
            if (!resultMap.ContainsKey(ind))
                resultMap[ind] = new List<string>();
            resultMap[ind].Add(entry.Key);
        }
 
        // Constructing final result with sorted emails
        List<List<string> > result
            = new List<List<string> >();
        foreach(var entry in resultMap)
        {
            List<string> email = entry.Value;
            email.Sort();
            email.Insert(0, accounts[entry.Key][0]);
            result.Add(email);
        }
 
        return result;
    }
 
    public static void Main(string[] args)
    {
        // Provided Input
        List<List<string> > details
            = new List<List<string> >{
                  new List<string>{
                      "John", "johnsmith@mail.com",
                      "john_newyork@mail.com" },
                  new List<string>{ "John",
                                    "johnsmith@mail.com",
                                    "john00@mail.com" },
                  new List<string>{ "Mary",
                                    "mary@mail.com" },
                  new List<string>{ "John",
                                    "johnnybravo@mail.com" }
              };
 
        // Call the MergeDetails function and print the
        // result
        List<List<string> > result = MergeDetails(details);
        foreach(var detail in result)
        {
            foreach(var item in detail)
            {
                Console.Write(item + " ");
            }
            Console.WriteLine();
        }
    }
}
 
// This code is contributed by Susobhan Akhuli


Javascript




// Function to find the root of the union find array
function find(unionFind, ind) {
    while (unionFind[ind] !== ind)
        ind = unionFind[ind];
    return ind;
}
 
// Function to merge the accounts
function mergeDetails(accounts) {
    const m = new Map();
    const unionFind = new Array(accounts.length).fill(0);
    const resMap = new Map();
 
    // Initializing union find array and mapping email to account index
    for (let i = 0; i < accounts.length; i++) {
        unionFind[i] = i;
        for (let j = 1; j < accounts[i].length; j++) {
            if (m.has(accounts[i][j])) {
                const root1 = find(unionFind, i);
                const root2 = find(unionFind, m.get(accounts[i][j]));
                unionFind[root1] = root2;
            } else {
                m.set(accounts[i][j], unionFind[i]);
            }
        }
    }
 
    // Mapping root index to merged emails
    for (const [email, index] of m) {
        const ind = find(unionFind, index);
        if (!resMap.has(ind)) {
            resMap.set(ind, []);
        }
        resMap.get(ind).push(email);
    }
 
    // Constructing final result with sorted emails
    const result = [];
    for (const [index, emails] of resMap) {
        const email = emails.sort();
        email.unshift(accounts[index][0]);
        result.push(email);
    }
 
    return result;
}
 
// Provided Input
const details = [
    ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
    ["John", "johnsmith@mail.com", "john00@mail.com"],
    ["Mary", "mary@mail.com"],
    ["John", "johnnybravo@mail.com"]
];
 
// Call the mergeDetails function and print the result
const result = mergeDetails(details);
for (const detail of result) {
    console.log(detail.join(" "));
}


Python3




# Python program for the above approach
def find(union_find, ind):
    while union_find[ind] != ind:
        ind = union_find[ind]
    return ind
 
def merge_details(accounts):
    m = {}
    union_find = list(range(len(accounts)))
    res_map = {}
 
    # Initializing union find array and mapping email to account index
    for i, account in enumerate(accounts):
        for j in range(1, len(account)):
            email = account[j]
            if email in m:
                root1 = find(union_find, i)
                root2 = find(union_find, m[email])
                union_find[root1] = root2
            else:
                m[email] = union_find[i]
 
    # Mapping root index to merged emails
    for email, index in m.items():
        ind = find(union_find, index)
        if ind not in res_map:
            res_map[ind] = []
        res_map[ind].append(email)
 
    # Constructing final result with sorted emails
    res = []
    for index, emails in res_map.items():
        account = [accounts[index][0]] + sorted(emails)
        res.append(account)
 
    return res
 
# Provided Input
details = [
    ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
    ["John", "johnsmith@mail.com", "john00@mail.com"],
    ["Mary", "mary@mail.com"],
    ["John", "johnnybravo@mail.com"]
]
 
# Call the mergeDetails function and print the result
result = merge_details(details)
for detail in result:
    print(' '.join(detail))
 
# This code is contributed by Susobhan Akhuli


Output

John john00@mail.com john_newyork@mail.com johnsmith@mail.com 
Mary mary@mail.com 
John johnnybravo@mail.com 

Time Complexity: O(n * a), where n is number of accounts or students and a is average number of emails per account.
Auxiliary Space: O(n * a)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads