Showing posts with label Coinbase. Show all posts
Showing posts with label Coinbase. Show all posts

Sunday, May 8, 2022

[Coinbase] Get Resource with max frequency of every 5 minutes interval

Problem: You are given the logs in [timestamp, user, resource] format which indicates the timestamp  when user access a resource. You need to provide the most accessed resource of every 5 minute window. You can say the timestamp is representing minutes here.

Example:
Input: logs = ["1", "user_1", "resource_1"],
["3", "user_2", "resource_2"],
["4", "user_1", "resource_3"],
["7", "user_2", "resource_3"],
["8", "user_1", "resource_3"]
Output: [resource_1, resource_3, resource_3]


Approach: If you see this problem is similar to designing data structure with increment, decrement, max and min at constant time. We can mix sliding window approach with it to solve this problem.


Implementation in C#:

    private static void PrintMaxOfEvery5Mins(Log[] logs)
    {
        Array.Sort(logs, (l1, l2) => {
           return l1.Timestamp.CompareTo(l2.Timestamp);
        });
      
        Dictionary<string, LinkedListNode<int>> resourceToFreqNodeMap = new Dictionary<string, LinkedListNode<int>>();
        Dictionary<int, HashSet<string>> freqToResourcesMap = new Dictionary<int, HashSet<string>>();
        LinkedList<int> frequenciesList = new LinkedList<int>();
        Queue<Log> queue = new Queue<Log>();
        
        foreach(var log in logs)
        {
            if (queue.Count > 0 && log.Timestamp - queue.Peek().Timestamp >= 5)
            {
                Console.WriteLine(GetMaxOf5Mins(freqToResourcesMap, frequenciesList));
                while (queue.Count > 0 && log.Timestamp - queue.Peek().Timestamp >= 5)
                {
                    DecrementCount(queue.Dequeue().Resource, resourceToFreqNodeMap, freqToResourcesMap, frequenciesList);
                }
            }
            queue.Enqueue(log);
            IncrementCount(log.Resource, resourceToFreqNodeMap, freqToResourcesMap, frequenciesList);
        }
        
        if (queue.Count > 0)
        {
            Console.WriteLine(GetMaxOf5Mins(freqToResourcesMap, frequenciesList));
        }
    }
    
    private static void IncrementCount(string resource,
                                      Dictionary<string, LinkedListNode<int>> resourceToFreqNodeMap,
                                      Dictionary<int, HashSet<string>> freqToResourcesMap,
                                      LinkedList<int> frequenciesList)
    {
        if (!resourceToFreqNodeMap.ContainsKey(resource))
        {
            AddResource(resource, resourceToFreqNodeMap, freqToResourcesMap, frequenciesList);
        }
        else
        {
            IncResourceCount(resource, resourceToFreqNodeMap, freqToResourcesMap, frequenciesList);
        }
    }
    
    private static void AddResource(string resource,
                                      Dictionary<string, LinkedListNode<int>> resourceToFreqNodeMap,
                                      Dictionary<int, HashSet<string>> freqToResourcesMap,
                                      LinkedList<int> frequenciesList)
    {
        if (!freqToResourcesMap.ContainsKey(1))
        {
            freqToResourcesMap[1] = new HashSet<string>();
            frequenciesList.AddFirst(1);
        }
        
        freqToResourcesMap[1].Add(resource);
        resourceToFreqNodeMap[resource] = frequenciesList.First;
    }
    
    private static void IncResourceCount(string resource,
                                      Dictionary<string, LinkedListNode<int>> resourceToFreqNodeMap,
                                      Dictionary<int, HashSet<string>> freqToResourcesMap,
                                      LinkedList<int> frequenciesList)
    {
        var freqNode = resourceToFreqNodeMap[resource];
        int currCount = freqNode.Value;
        int newCount = currCount + 1;
        if (!freqToResourcesMap.ContainsKey(newCount))
        {
            freqToResourcesMap[newCount] = new HashSet<string>();
            frequenciesList.AddAfter(freqNode, newCount);
        }
        freqToResourcesMap[newCount].Add(resource);
        freqToResourcesMap[currCount].Remove(resource);
        resourceToFreqNodeMap[resource] = freqNode.Next;
        if (freqToResourcesMap[currCount].Count == 0)
        {
            frequenciesList.Remove(freqNode);
            freqToResourcesMap.Remove(currCount);
        }
    }
    
    private static void DecrementCount(string resource,
                                      Dictionary<string, LinkedListNode<int>> resourceToFreqNodeMap,
                                      Dictionary<int, HashSet<string>> freqToResourcesMap,
                                      LinkedList<int> frequenciesList)
    {
        var freqNode = resourceToFreqNodeMap[resource];
        int currCount = freqNode.Value;
        int newCount = currCount - 1;
        if (newCount > 0)
        {
            if (!freqToResourcesMap.ContainsKey(newCount))
            {
                freqToResourcesMap[newCount] = new HashSet<string>();
                frequenciesList.AddBefore(freqNode, newCount);
            }
            freqToResourcesMap[newCount].Add(resource);
            resourceToFreqNodeMap[resource] = freqNode.Previous;
        }
        
        freqToResourcesMap[currCount].Remove(resource);
        if (freqToResourcesMap[currCount].Count == 0)
        {
            frequenciesList.Remove(freqNode);
            freqToResourcesMap.Remove(currCount);
        }
        if (newCount == 0)
        {
            resourceToFreqNodeMap.Remove(resource);
        }
    }
    
    private static string GetMaxOf5Mins(Dictionary<int, HashSet<string>> freqToResourcesMap,
                                      LinkedList<int> frequenciesList)
    {
        if (frequenciesList == null)
        {
            return string.Empty;
        }
        return freqToResourcesMap[frequenciesList.Last.Value].First();
    }
}


class Log
{
    public int Timestamp {get; private set;}
    public string User {get; private set;} 
    public string Resource {get; private set;}
    
    public Log(string timestamp, string user, string resource)
    {
        this.Timestamp = int.Parse(timestamp);
        this.User = user;
        this.Resource = resource;
    }
}

Complexity: O(n)

Thursday, May 5, 2022

[Coinbase] Extract user session from logs

Problem: You are given the logs in [timestamp, user, resource] format which indicates the timestamp  when user access a resource. A user session is defined as the earliest timestamp and the latest timestamp when user accessed any resource.

Extract all the user sessions from the logs.

Example:

Input: logs = ["58523", "user_1", "resource_1"],
["62314", "user_2", "resource_2"],
["54001", "user_1", "resource_3"],
["54060", "user_2", "resource_3"],
["54359", "user_1", "resource_3"]
Output: user_1 -> (54001 - 58523)
user_2 -> (54060 - 62314)


Approach: It's an easy problem to solve by using hashing. You can understand the approach by just looking at the code.


Implementation in C#:

    private static Dictionary<string, List<int>> GetUserSession(Log[] logs)

    {

        Dictionary<string, List<int>> userSessions = new Dictionary<string, List<int>>();

        foreach (Log log in logs)

        {

            if (!userSessions.ContainsKey(log.User))

            {

                userSessions[log.User] = new List<int>();

            }

            // User session is already recorded for this user, update if required

            if (userSessions[log.User].Count == 2)

            {

                // current timestamp is lower than existing session's first timestamp

               // Make current as first timestamp for this user session

                if (log.Timestamp < userSessions[log.User][0])

                {

                    userSessions[log.User][0] = log.Timestamp;

                }

                // current timestamp is greater than existing session's last timestamp

               // Make current as last timestamp for this user session

                else if (log.Timestamp > userSessions[log.User][1])

                {

                    userSessions[log.User][1] = log.Timestamp;

                }

            }

            // current timestamp is lower than first timestamp of this user session.

            else if (userSessions[log.User].Count == 1 && log.Timestamp < userSessions[log.User][0])

            {

                userSessions[log.User].Insert(0, log.Timestamp);

            }

            else

            {

                userSessions[log.User].Add(log.Timestamp);

            }

        }

        return userSessions;

    }

    

    private static void PrintUserSession(Dictionary<string, List<int>> userSessions)

    {

        foreach(var session in userSessions)

        {

            string output = session.Key + " -> (" + session.Value[0] + " - ";

            if (session.Value.Count == 1)

            {

                output += session.Value[0] + ")";

            }

            else

            {

                output += session.Value[1] + ")";

            }

            Console.WriteLine(output);

        }

    }


    class Log

    {

        public int Timestamp {get; private set;}

        public string User {get; private set;} 

        public string Resource {get; private set;}    

        public Log(string timestamp, string user, string resource)

        {

            this.Timestamp = int.Parse(timestamp);

            this.User = user;

            this.Resource = resource;

        }

    }


Complexity: O(n)