Skip to content

Instantly share code, notes, and snippets.

@perymimon
Last active January 13, 2021 13:10
Show Gist options
  • Save perymimon/836a0913ce8a3dd1f94028f1fd656018 to your computer and use it in GitHub Desktop.
Save perymimon/836a0913ce8a3dd1f94028f1fd656018 to your computer and use it in GitHub Desktop.

Talkspace Full-stack candidate exercise

Guidelines: The following exercise questions should take 1-2 hours, Put your answers after each question - try to give some short explanation for your

Candidate Name: Pery Date: 12/02/2020

Questions:

1 - Algorithm

Consider an array containing positive integers sorted in ascending order. A “Lead Number” of this array is an integer that repeats itself more than half of the array length Example: Example1 = [1,2,3,4,4,4,4,4,4,4,7,7,10] , the number ‘4’ repeats 7 times out of 13 hence it is the “Lead” Example2 = [1,4,4,6,6,6,6,9,9,13], the number ‘6’ repeats 4 times out of of 10 which is not enough - hence this array has no lead (-1)

Write a function that receives an array and returns the lead (or -1 if there is none), this should be as efficient as possible (time and memory), please add a short explanation and the claimed complexity. Can this be optimized to run in less than O(n)?

solution

/**
Real-lead must be the value in the middle of the array (or one left or one right from the middle)
so we know the potenial lead from the begging. we just need to check the edges of that series
and understand if it long from arra.length or not. the faster way for found the edges is quick search ( jumping half every time )
so the complexity should be O(log(n))
**/

function getLead(sortedArr) {
   const {length} = sortedArr;
   var arrayMiddle = Math.floor(length / 2) // get the middle of the array
   const pl = sortedArr[arrayMiddle] // the the potential lead
   var leftEdge, rightEdge;
   // find the left edge
   let s = 0, e = arrayMiddle, m; // start, end

   do {
       m = s + Math.floor((e - s) / 2);
       if (sortedArr[m] == pl) e = m;
       else s = m;
   } while (e - s > 1)

   leftEdge = e;

   s = arrayMiddle;
   e = length - 1;

   do {
       m = s + Math.ceil((e - s) / 2);
       if (sortedArr[m] == pl) s = m;
       else e = m;
   } while (e - s > 1);

   rightEdge = s;

   if ((rightEdge - leftEdge) + 1 > length / 2)
       return pl;
   return -1;
}     

2 - consider the following code:

code

  • What is the output of this code

Promise that resolve to [1,4,9,16,25,36,49,64,81, 100]
note: new Array(10) return empty array, [... new Array(10)] return [10 x undefined]

  • Write a function to replace ‘sampleFunction’ - that receives an object with 3 numbers and returns the minimal (this function should be typeScript valid!)
function sampleFunction(obj:{n1:number,n2:number, n3:number }){
   return Promise.resolve(Object.values(obj).sort()[0])
}
  • Refactor ‘doParallel’ to handle the ‘TODO’ comment properly
    Note: consider that some task instances can be significantly slower than otherss

After considered with Yaeer, I Understand that the "slower" is just about IO operation so the code write right because if sampleFunction() return promise ( even Promise.resolve) The Code guaranteed to be async ( I should know, I implemented Promise ) And IO operation runs under the hood on different threads If we want REAL concurrency for intensive task in NodeJS we can do something like that:

Note from nodejs doc:
"Workers (threads) are useful for performing CPU-intensive JavaScript operations. They will not help much with I/O-intensive work. Node.js’s built-in asynchronous I/O operations are more efficient than Workers can be."

//concurrency.js
const {    Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
  async function doParallel<T, S>(
          func: (args: T) => Promise<S>,
          args: T[],
          paralllelism = 10
  ): promise<S[]> {
    const results = await AsyncQueue(args, paralllelism, async function (arg) {
      return new Promise((res, rej) => {
        var worker = new Worker(__filename, {
          workerData: {
            func: func.toString(),
            arg
          }
        });
        worker.once('message', res);
        worker.once('exit', rej);
        worker.postMessage(arg)
      });
    })
  
    return results;
  
  }
}else /** worker **/{
  /** parsing the function **/
  const func = new Function('return ' + workerData.func)();
  func(workerData.arg)
          .then(result => parentPort.postMessage(result))
          .catch(err => parentPort.postMessage(err));
    
}
// should be in helper/async-queue.js 
function AsyncQueue(args, maxNumOfWorkers, task) {
    var numOfWorkers = 0;
    var argIndex = 0;
    var results = [];
    return new Promise(done => {
        const handleResult = index => result => {
            results[index] = result;
            numOfWorkers--;
            getNextTask();
        };

        function getNextTask() {
            if (numOfWorkers === 0 && argIndex === args.length) {
                done(results);
                return;
            }
            while (numOfWorkers < maxNumOfWorkers && argIndex < args.length) {
                task(args[argIndex])
                    .then(handleResult(argIndex))
                    .catch(handleResult(argIndex));
                argIndex++;
                numOfWorkers++;
            }

        }

        getNextTask();
    });
}

shorter version, without need of Async Queue

//concurrency.js
const {Worker, isMainThread, parentPort, workerData} = require('worker_threads');

if (isMainThread) {
  async function doParallel<T, S>(
          func: (args: T) => Promise<S>,
          args: T[],
          paralllelism = 10
  ): promise<S[]> {
        return new Promise(function (done, rej) {
            args = [...Object.entries(args)];
            var results = [];
            var counterDown = args.length;
            const workers = [];
            maxNumOfWorkers = Math.min(maxNumOfWorkers, args.length)
            Array.from(Array(maxNumOfWorkers), _ => {
                let worker = new Worker(__filename, {workerData: func.toString()});
                workers.push(worker);
                worker.on('message', function ([i, result]) {
                    results[i] = result;
                    workers.push(worker)
                    if (--counterDown === 0)
                        return done(results)
                    runWorkers();
                })
            })

            function runWorkers() {
                while (args.length && workers.length) {
                    workers.pop().postMessage(args.pop())
                }

            }

            runWorkers();
        })
    }
} else  /** worker **/ {
    /** parsing the function **/
    const func = new Function('return ' + workerData)();
    parentPort.on("message", function ([i, arg]) {
        func(arg)
            .then(result => parentPort.postMessage([i, result]))
            .catch(err => parentPort.postMessage([i, err]));
    })
}

3 - sql

SQL - Consider the following database schema containing 2 tables: user_cart

cart_id user_id created_at closed_at
1 123 2020-10-01 13:10:05 NULL
2 125 2020-10-02 14:10:05 NULL

cart_items

item_id | cart_id | barcode | item_name | added_at 1 | 1 |12344242 | XXXX |2020-10-01 13:11:00 2 | 1 |12344242 | XXXX |2020-10-01 13:11:05 3 | 1 |542342331 | YYYY |2020-10-01 13:12:10

  • Write a SQL query that returns all the user_cart rows that added item with barcode X on the last day (one row per cart!)
SELECT * 
FROM cart_items
WHERE cart_id IN (
    SELECT cart_id, item_name, added_at
    FROM cart_items
    WHERE item_name = "XXXX" AND 
        DATE(cart_items.added_at) >  (NOW() - INTERVAL 1 DAY )
)
                    
  • Write a SQL query that returns all the items of the first 10 carts the took item with barcode X in the last week
-- items IN carts with XXXX in the last week sorted by `added_at` 
 SELECT * 
 FROM cart_items
 WHERE cart_id IN (
    SELECT DISTINCT cart_id 
    FROM cart_items
    WHERE ( item_name = "XXXX"
              AND DATE(added_at) >  (NOW() - INTERVAL 7 DAY )
           )
    SORT BY added_at
    LIMIT 10
 )
    

5 - react

Consider the following React Component: reactCode This implementation holds a potential performance issue, can you suggest improvements?

new UserAPI create a new instance on every run of the UserList Componnect so useEffect will run every time UserList Componnect run (event if no change will be on the screen) then it fire fetchUsers() so beside the performance issue there is a race condition on the result.

simple solution is take out the cost userAPI = ... out from the component factory; also return users.map( user=> <p>{user.name}</p> ) not contain key on p so there is potential of unnecessary render of the list to screen every time the list update.

Consider the following code: what will happen when the “Increment” (or “Decrement”) button is clicked? Please explain why that is and if there is a way to improve the code?

import React, {createContext, useState, useContext} from 'react';

interface CounterState {
   count: number;
   increment?: () => void;
   decrement?: () => void;
   hello: string;
}

const initialState: CounterState = {
   count: 0,
   increment: undefined,
   decrement: undefined,
   hello: 'Hello world',
};
const CounterContext = createContext<CounterState>(initialState);
const CounterProvider = ({children}) => {
   const [count, setCount] = useState(0);
   const [hello] = useState('Hello world');
   const increment = () => setCount((counter) => counter + 1);
   const decrement = () => setCount((counter) => counter - 1);
   const value = {
       count,
       increment,
       decrement,
       hello,
   };
   return <CounterContext.Provider value = {value} > {children} < /CounterContext.Provider>;
};
const SayHello = () => {
   const {hello} = useContext(CounterContext);
   console.log('[SayHello] rendered');
   return <h1>{hello} < /h1>;
};
const IncrementCounter = () => {
   const {increment} = useContext(CounterContext);
   console.log('[IncrementCounter] rendered');
   return <button onClick = {increment} > Increment < /button>;
};
const DecrementCounter = () => {
   console.log('[DecrementCounter] rendered');
   const {decrement} = useContext(CounterContext);
   return <button onClick = {decrement} > Decrement < /button>;
};
const ShowResult = () => {
   console.log('[ShowResult] rendered');
   const {count} = useContext(CounterContext);
   return <h1>{count} < /h1>;
};
const App = () => (
   <CounterProvider>
       <SayHello / >
   <ShowResult / >
   <IncrementCounter / >
   <DecrementCounter / >
   </CounterProvider>
);
export default App;

Explanation:
Every time decrement() or increment() running, all the components that used CounterContext unnecessary render so SayHello Componnet, IncrementCounter Componnet, DecrementCounter Componnet, will render again with the same result. the behaviour, why is that can found here: useContext

One solution is create separate context for hello and count.
That will save up from render SayHello Componnet

Other solution is not use contex approach at all. look on the entire file as a module that provides a count and supply useCount to bring count data to all components that need it

Other solution is supply Our Contex with special methods to update the context values.

Examples:

/** file : count.js **/
import React, {useEffect, useState} from 'react';

let setters = new Set();

function Setter(fn) {
    return function (...args) {
        for (let set of setters) {
            set(fn)
        }
    }
}

export const increment = Setter((counter) => counter + 1);
export const decrement = Setter((counter) => counter - 1);

export function useCount(initValue = 0) {
    const [count, setCount] = useState(initValue);
    useEffect(_ => {
        setters.add(setCount)
        return () => setters.delete(setCount)
    }, [setCount]);
    return count;
}
/** file : helloContext.js **/
import React, {createContext, useState, useContext} from 'react';

/* separete context for Hello, with new approch of actions: */
interface HelloState {
    hello: string;
}

export default
const HelloContext = ({children}) => {
    const context = createContext<CounterState>(HelloState);
    const [hello, setHello] = useState(HelloState);
    const value = {
        hello,
    };
    return {
        setHello: setHello,
        Provider: <context.Provider value = {value} > {children} < /context.Provider>;
    }
};
import React, {useEffect, createContext, useState, useContext} from 'react';
import HelloContext from './helloContext'
import {useCount, increment, decrement} from './count'


const SayHello = () => {
    const {hello} = useContext(HelloProvider);
    console.log('[SayHello] rendered');
    return <h1>{hello} < /h1>;
};
const IncrementCounter = () => {
    console.log('[IncrementCounter] rendered');
    return <button onClick = {increment} > Increment < /button>;
};
const DecrementCounter = () => {
    console.log('[DecrementCounter] rendered');
    return <button onClick = {decrement} > Decrement < /button>;
};
const UpdateHello = () => {
    console.log('[UpdateHello] rendered');
    return <button onClick = {HelloContext.setHello("Hello universe")} > Decrement < /button>;
}


const ShowResult = () => {
    console.log('[ShowResult] rendered');
    const {count} = useCount();
    return <h1>{count} < /h1>;
};
const App = () => (
    <HelloContext.Provider>
        <SayHello / >
    <ShowResult / >
    <UpdateHello / >
    <IncrementCounter / >
    <DecrementCounter / >
    </HelloContext.Provider>
);
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment