Who is my boss’s boss? [Data Analytics Challenge – 001]

Share

Facebook
Twitter
LinkedIn

Let’s try something different. I will share a data analytics challenge here. Post your solutions in the comments.

Our first challenge involves Employee Data Analysis. You can score maximum of 35 points.

Imagine you have employee data in this “staff” table (in Excel / Power BI / SQL / Python or whatever tool you fancy)

Download sample data file (it has all the 28 employees) or use the below CSV.

Emp ID	Name	Reports to
TO-0002	Jim Halpert	TO-0007
TO-0004	Pam Beesly	TO-0015
TO-0007	Michael Scott	TO-0067
TO-0009	Erin Hannon	TO-0004
TO-0010	Jan Levinson	TO-0067
TO-0013	Karen Filippelli	TO-0076
TO-0015	Dwight Schrute	TO-0007
TO-0018	Andy Bernard	TO-0002
TO-0021	Cathy Simms	TO-0002
TO-0024	Kevin Malone	TO-0035
TO-0028	Creed Bratton	TO-0007
TO-0030	Toby Flenderson	TO-0067
TO-0031	Kelly Kapoor	TO-0028
TO-0033	Robert California	TO-0067
TO-0035	Angela Martin	TO-0007
TO-0039	Stanley Hudson	TO-0007
TO-0043	Ryan Howard	TO-0031
TO-0046	Oscar Martinez	TO-0035
TO-0050	Meredith Palmer	TO-0028
TO-0051	Gabe Lewis	TO-0072
TO-0055	Phyllis Vance	TO-0030
TO-0059	Roy Anderson	TO-0004
TO-0063	Nellie Bertram	TO-0030
TO-0066	Darryl Philbin	TO-0039
TO-0067	David Wallace	TO-0072
TO-0068	Pete Miller	TO-0015
TO-0072	Jo Bennet	
TO-0076	Todd Parker	TO-0002

1. How many people are directly reporting to my boss? (5pts)

The first question is simple. For a given employee ID (say TO-0021, Cathy Simms), how many people are directly reporting to their boss (TO-0002, Jim Halpert)? The answer should be 3.

Write the necessary Excel formula / DAX / Power Query / SQL or Python code to find the answer.

2. Who is my boss’s boss? (10pts)

We are going to level up. For a given employee ID (say TO-0021, Cathy Simms), find out their boss’s boss. The answer should be TO-0007, Michael Scott.

3. What is my reporting chain? (Score=20pts)

For a given employee (say TO-0021, Cathy Simms), print their entire reporting chain, delimited by the symbol ->.

Expected Answer is:
Cathy->Jim->Michael->David->Jo

Note: You may assume a maximum depth of 7 nodes if that helps.

Download Sample File

Grab the sample data file (Excel format) here. Use it to solve the problems. Alternatively, I have pasted the data above. Copy it and paste it in Python or SQL.

Post your answers below.

Leave a comment with your solutions / approach. All the best

Want more?

Check out my Excel Homework Tag page for more challenges and problems.

Facebook
Twitter
LinkedIn

Share this tip with your colleagues

Excel and Power BI tips - Chandoo.org Newsletter

Get FREE Excel + Power BI Tips

Simple, fun and useful emails, once per week.

Learn & be awesome.

Welcome to Chandoo.org

Thank you so much for visiting. My aim is to make you awesome in Excel & Power BI. I do this by sharing videos, tips, examples and downloads on this website. There are more than 1,000 pages with all things Excel, Power BI, Dashboards & VBA here. Go ahead and spend few minutes to be AWESOME.

Read my storyFREE Excel tips book

Overall I learned a lot and I thought you did a great job of explaining how to do things. This will definitely elevate my reporting in the future.
Rebekah S
Reporting Analyst
Excel formula list - 100+ examples and howto guide for you

From simple to complex, there is a formula for every occasion. Check out the list now.

Calendars, invoices, trackers and much more. All free, fun and fantastic.

Advanced Pivot Table tricks

Power Query, Data model, DAX, Filters, Slicers, Conditional formats and beautiful charts. It's all here.

Still on fence about Power BI? In this getting started guide, learn what is Power BI, how to get it and how to create your first report from scratch.

57 Responses to “Who is my boss’s boss? [Data Analytics Challenge – 001]”

  1. Vit says:

    I ripped a recursive lambda solution from https://exceldashboardschool.com/lambda-function/recursive-examples/ to get the boss chain.
    Named lambda formula "CATEGORYCHAIN":

    =LAMBDA(prod,products,categories,
    LET(
    parent, XLOOKUP(prod, products, categories, ""),
    IF(parent="", TAKE(TEXTSPLIT(XLOOKUP(prod,[Emp ID],[Name])," "),1,1), TAKE(TEXTSPLIT(XLOOKUP(prod,[Emp ID],[Name])," "),1,1) & "->" & CATEGORYCHAIN(parent, products, categories))
    )
    )

    prod is the employee code, products is the [Emp ID] column, categories is the [Reports to] column

  2. John M. says:

    1. =COUNTIF([Reports to],[@[Reports to]])
    2. =XLOOKUP([@[Reports to]],[Emp ID],[Reports to],"",0)
    3. Got this far and then got dizzy trying to "recurse" up the chain:
    =LEFT([@Name],FIND(" ",[@Name])-1)&"->"&LEFT(XLOOKUP([@[Reports to]],[Emp ID],[Name],"",0),FIND(" ",XLOOKUP([@[Reports to]],[Emp ID],[Name],"",0))-1)&"->"&LEFT(XLOOKUP([@[Boss''Boss]],[Emp ID],[Name],"",0),FIND(" ",XLOOKUP([@[Boss''Boss]],[Emp ID],[Name],"",0)))

  3. Guray VURAL says:

    1)

    =COUNTIF([Reports to],[@[Reports to]])

    2)
    =CONCAT(XLOOKUP([@[Reports to]],[Emp ID],[Reports to]," "), ", ",

    XLOOKUP(XLOOKUP([@[Reports to]],[Emp ID],[Reports to],"None"),[Emp ID],[Name]," "))

    3)

    =CONCAT(TEXTBEFORE([@Name]," "),"->",
    IFERROR(TEXTBEFORE(XLOOKUP([@[Reports to]],[Emp ID],[Name],"")," "),""),"->",
    IFERROR(TEXTBEFORE(XLOOKUP(XLOOKUP([@[Reports to]],[Emp ID],[Reports to],""),[Emp ID],[Name])," "),""),"->",
    IFERROR(TEXTBEFORE(XLOOKUP(XLOOKUP(XLOOKUP((XLOOKUP([@[Reports to]],[Emp ID],[Reports to],"")),[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Name],"")," "),""))

  4. Alliance PEMOSSO says:

    Hey Sir, I only use Excel.

    Question 1:
    a) =COUNTIF(C2:C16, VLOOKUP("TO-0021", A2:C16, 3, FALSE))
    or simply
    b) =COUNT.IF(staff[Reports to];RECHERCHEV("TO-0021";staff[[Emp ID]:[Reports to]];3;FAUX))

    Question 2:
    =RECHERCHEV(RECHERCHEV(M14;staff[[Emp ID]:[Reports to]];3;FAUX);staff[[Emp ID]:[Reports to]];3;FAUX)
    To find another thing I can just change the col_index

    Question 3:
    I have not been able to do that in Excel.
    But I tried with this ?
    =RECHERCHEV(M14;staff[[Emp ID]:[Reports to]];2;FAUX)&"->"&RECHERCHEV(N14;staff[[Emp ID]:[Reports to]];2;FAUX)&"->"&RECHERCHEV(O14;staff[[Emp ID]:[Reports to]];2;FAUX)

  5. cesar mendoza says:

    The last one got me!

  6. Virendra Singh says:

    Hi Chandoo

    Had some fun doing the problems. I retired in 2019 and doing this was enjoyable. It could probably be done more efficiently with lambda functions.

    Thanks
    Virendra

  7. SOLUTION: First I added two names than there was not in the table: TO-0067 i named "Top Exec." and TO-0076 i named "Karen Filippelli's Boss"
     
    FIRST QUESTION:
    For a given employee ID (say TO-0021, Cathy Simms), how many people are directly reporting to their boss (TO-0002, Jim Halpert)?
    ANSWER:
    =COUNTA(CHOOSECOLS(FILTER(Staff,Staff[Boss ID]=XLOOKUP(B21,Staff[Name],Staff[Boss ID])),3))
    SECOND QUESTION:
    For a given employee ID (say TO-0021, Cathy Simms), find out their boss’s boss. The answer should be TO-0007, Michael Scott.
    ANSWER:
    Their boss’s boss is two levels up: 2
    Using Lambda formula: Assigned to function name: NthManagerName
    =LAMBDA(empID, n,
    LET(
    ids, Staff[Emp ID],
    boss, Staff[Boss ID],
    names, Staff[Name],
    upID, REDUCE(empID, SEQUENCE(n), LAMBDA(acc, _, XLOOKUP(acc, ids, boss, ""))),
    XLOOKUP(upID, ids, names, "")
    )
    )
    Then Use
    =NthManagerName(B25,B27)
    THIRD QUESTION:
    For a given employee (say TO-0021, Cathy Simms), print their entire reporting chain, delimited by the symbol ->. Expected Answer is: Cathy->Jim->Michael->David->Jo
    ANSWER:
    Using the “NthManagerName” Lambda function over an sequence 1 to 20 and then TEXTJOIN the names:
    =TEXTJOIN(" -> " TRUE,Bosses)

  8. John M. says:

    My final solution for #3 (took awhile but realized I needed to take a different approach):
    =LET(
    ids, TRIM([Emp ID] & ""),
    mgrs, TRIM([Reports to] & ""),
    nm, [Name],
    firsts, IFERROR(LEFT(nm, FIND(" ", nm & " ") - 1), nm),
    me, TRIM([@[Emp ID]] & ""),
    chainIDs,
    HSTACK(
    me,
    SCAN(
    me,
    SEQUENCE(5),
    LAMBDA(prev,_,
    IF(prev="","", IFERROR(XLOOKUP(prev, ids, mgrs, ""), ""))
    )
    )
    ),
    chainNames, IFERROR(XLOOKUP(chainIDs, ids, firsts, ""), ""),
    TEXTJOIN("->", TRUE, chainNames)
    )

  9. Bob Pieciak says:

    I used the sledgehammer method.
    =staff[@[Emp ID]] in a cell
    =XLOOKUP(P4,$B:$B,$D:$D) in a neighboring cell
    Repeat until you run into #N/A
    Then
    =TEXTBEFORE(XLOOKUP(P4,$B:$B,$C:$C)," ") In a cell and copied until you hit #N/A
    Then
    =TEXTJOIN("->",TRUE,IF(ISNA(K4:O4),"",K4:O4)) in the Chain cell

  10. Sonya F says:

    1. I did a pivot table
    2. =XLOOKUP([@[Reports to]],A:A,B:B)
    3. =IFERROR(TEXTBEFORE([@[Mgr''sName]]," ")&"->"&TEXTBEFORE([@[SrMgr''sName]]," ")&"->"&TEXTBEFORE([@3rdLevel]," ")&"->"&TEXTBEFORE([@4thLevel]," "),"")

  11. JP DAVY says:

    1. =COUNTIFS([Boss ID],[@[Boss ID]])
    2. =LET(MyVal,XLOOKUP([@[Boss ID]],[Emp ID],[Boss ID],""),IF(OR(MyVal=0,MyVal=""),"Nobody!",MyVal))
    3. Based on 8 possible levels WITH Helper Columns …
    =TEXTJOIN(" -> ",TRUE,XLOOKUP(LET(MyRow, HSTACK([@[Emp ID]],Staff[@[Boss ID]:[Reports to L8]]),FILTER(MyRow,NOT(ISNUMBER(MyRow))*(MyRow""),"No data!")),[Emp ID],TEXTBEFORE([Name]," "),""))
    NB: I left the [# Reports to my boss] column at its place, what annoyed me a little 😉
    VBA must be much much easier ! Don't know Python (yet) and long to discover/learn recursive Lambda 😉

  12. Malleswara Reddy says:

    Here are the SQL queries

    --select * from staff;
    --1. How many people are directly reporting to my boss?
    select * from staff
    where [Reports to]=(select [Reports to]
    from staff
    where [Emp ID]='TO-0021');
    --2. Who is my boss’s boss?
    select * from staff where [Emp ID]=(
    select [Reports to] from staff where [Emp ID]=(
    select [Reports to]
    from staff
    where [Emp ID]='TO-0021'));

    --3. What is my reporting chain?

    --DECLARE @EmployeeID varchar = 'TO-0021';
    with emp_tree as(
    select [Reports to] as ManagerID, [Emp ID] as EmpID,Name as EmpName,1 as LEVEL
    from staff where [Emp ID] = 'TO-0002'
    union all
    select s1.[Reports to] as ManagerID,s1.[Emp ID] as EmpID,Name as EmpName,(e.LEVEL+1) as LEVEL
    from staff s1
    join emp_tree e on e.ManagerID = s1.[Emp ID]
    )

    select STRING_AGG(left(EmpName,CHARINDEX(' ',EmpName)),'->') as reporting_chain from emp_tree;

  13. Giorgio Berardi says:

    1) =COUNTIF([Reports to],[@[Reports to]])
    2) =XLOOKUP([@[Reports to]],[Emp ID],[Reports to])
    3) got a bit lost while trying to write a lambda function

  14. Nagaraj says:

    -- 1st submission

    select e1.EmpID,e1.Name,e1.BossID as reports from Project.dbo.Employees as e inner join Project.dbo.Employees as e1
    on e.EmpID = e1.BossID
    where e.EmpID = 'TO-0002'

    -- 2nd mission

    DECLARE @EmpID VARCHAR(10) = 'TO-0021';

    select fact.EmpID,fact.Name,boss.Name as reportingmanager, bb.Name as Headmanager from Project.dbo.Employees as fact

    left join Project.dbo.Employees as boss on fact.BossID = boss.EmpID

    left join Project.dbo.Employees as bb on boss.BossID = bb.EmpID

    where fact.EmpID = @EmpID

  15. Danny says:

    #1 & 2 are trivial, but here is my solution for 3.
    Thought about doing recursion, but settled on using Scan instead, set up that it can work even if the whole table is a line of reporting.
    =LET(
    output,SCAN([@[Emp ID]],SEQUENCE(ROWS([Emp ID])),LAMBDA(id,boss,XLOOKUP(id,[Emp ID],[Reports to],0))),
    cleaned,FILTER(output,output0),
    bosses,VSTACK([@Name],XLOOKUP(cleaned,[Emp ID],[Name])),
    TEXTJOIN("->",,TEXTBEFORE(bosses," ")))

    • Julian says:

      Hi Danny
      Can I ask two dumb questions about your elegant solution (I'm only just starting out learning about recursive-type techniques):
      1. Firstly I'm getting a #Name? error, but I literally copied your code (and only had to update the table names with underscores for the formula to be accepted)
      2. I almost understand the formula (if I did, I would be able to solve #1!), but can you explain where output0 comes from please? Is this just the first array element from SCAN?
      Many thanks in advance, Julian

  16. Bhavik Khatri says:

    Answers in PQ
    Q1
    let
    // Load source data
    Source = Data,

    // Join employees to their direct bosses
    WithBoss = Table.NestedJoin(Source, {"Reports to"}, Source, {"Emp ID"}, "Boss_Data", JoinKind.LeftOuter),

    // Select employee name and boss details only
    EmployeeBoss = Table.SelectColumns(WithBoss, {"Name", "Boss_Data"}),

    // Expand boss details to get boss ID and name
    ExpandedBoss = Table.ExpandTableColumn(EmployeeBoss, "Boss_Data", {"Emp ID", "Name"}, {"Boss_ID", "Boss_Name"}),

    // Group by boss to get employee count and list of employees
    GroupedByBoss = Table.Group(
    ExpandedBoss,
    {"Boss_ID", "Boss_Name"},
    {
    {"Employee_Count", each Table.RowCount(_), Int64.Type},
    {"Employee_List", each Text.Combine([Name], ", "), type text}
    }
    ),

    // Sort by boss ID ascending
    SortedResult = Table.Sort(GroupedByBoss, {{"Boss_ID", Order.Ascending}})
    in
    SortedResult

    Q2)
    let
    // Load source data
    Source = Data,

    // Join to get direct boss details
    WithBoss = Table.NestedJoin(
    Source, {"Reports to"},
    Source, {"Emp ID"},
    "Boss_Data", JoinKind.LeftOuter
    ),
    ExpandedBoss = Table.ExpandTableColumn(
    WithBoss,
    "Boss_Data",
    {"Emp ID", "Name", "Reports to"},
    {"Boss_ID", "Boss_Name", "Boss_ReportsTo"}
    ),

    // Join to get boss's boss details
    WithBossBoss = Table.NestedJoin(
    ExpandedBoss, {"Boss_ReportsTo"},
    Source, {"Emp ID"},
    "BossBoss_Data", JoinKind.LeftOuter
    ),
    ExpandedBossBoss = Table.ExpandTableColumn(
    WithBossBoss,
    "BossBoss_Data",
    {"Emp ID", "Name"},
    {"BossBoss_ID", "BossBoss_Name"}
    ),

    // Select relevant columns
    SelectedColumns = Table.SelectColumns(
    ExpandedBossBoss,
    {"Emp ID", "Name", "Boss_ID", "Boss_Name", "BossBoss_ID", "BossBoss_Name"}
    ),

    // Sort by employee ID
    Result = Table.Sort(SelectedColumns, {{"Emp ID", Order.Ascending}})
    in
    Result

    Q3
    let
    // Prepare data with Emp ID and ReportsTo as text for consistent matching
    EmployeeData = Table.TransformColumns(
    Table.SelectColumns(Data, {"Emp ID", "Name", "Reports to"}),
    {{"Emp ID", Text.From, type text}, {"Reports to", Text.From, type text}}
    ),

    // Retrieve employee record by Emp ID
    GetRecord = (id as text) =>
    let row = Table.SelectRows(EmployeeData, each [Emp ID] = id) in
    if Table.IsEmpty(row) then null else row{0},

    // Iteratively build reporting chain using List.Accumulate with early exit
    BuildChain = (startID as text) =>
    let
    MaxDepth = 50,
    AccFunc = (acc, _) =>
    let
    chain = acc{0},
    currentID = acc{1},
    visited = acc{2},
    stop = currentID = null or currentID = "" or List.Contains(visited, currentID),
    rec = if stop then null else GetRecord(currentID),
    name = if rec = null then null else rec[Name],
    nextID = if rec = null then null else rec[Reports to],
    newChain = if stop then chain else List.Combine({chain, {name}}),
    newVisited = if stop then visited else List.Combine({visited, {currentID}})
    in
    {newChain, nextID, newVisited},
    result = List.Accumulate(List.Repeat({0}, MaxDepth), {{}, startID, {}}, AccFunc)
    in
    result{0},

    // Add Reporting Chain column, join names with " -> "
    AddChainCol = Table.AddColumn(EmployeeData, "Reporting Chain", each Text.Combine(List.RemoveNulls(BuildChain([Emp ID])), " -> "), type text),

    // Sort by Emp ID ascending
    Sorted = Table.Sort(AddChainCol, {{"Emp ID", Order.Ascending}})
    in
    Sorted

  17. Bhavik Khatri says:

    Answer Python
    Q1)
    # Source Data
    df = xl("Data[#All]", headers=True)

    # Merge with itself to get boss details
    merged = df.merge(
    df[['Emp ID', 'Name']],
    left_on='Reports to',
    right_on='Emp ID',
    how='left',
    suffixes=('', '_Boss')
    )

    # Rename for clarity
    merged.rename(columns={'Emp ID_Boss': 'Boss_ID', 'Name_Boss': 'Boss_Name'}, inplace=True)

    # Group by Boss and aggregate
    grouped = merged.groupby(['Boss_ID', 'Boss_Name']).agg(
    Employee_Count=('Name', 'count'),
    Employee_List=('Name', lambda x: ', '.join(x))
    ).reset_index()

    Q2)
    # Source Data
    df = xl("Data[#All]", headers=True)

    # Merge to get direct boss details
    merged_with_boss = df.merge(
    df[['Emp ID', 'Name', 'Reports to']],
    left_on='Reports to',
    right_on='Emp ID',
    how='left',
    suffixes=('', '_Boss')
    )

    # Rename for clarity
    merged_with_boss.rename(columns={
    'Emp ID_Boss': 'Boss_ID',
    'Name_Boss': 'Boss_Name',
    'Reports to_Boss': 'Boss_ReportsTo'
    }, inplace=True)

    # Merge again to get the boss's boss details
    merged_with_bossboss = merged_with_boss.merge(
    df[['Emp ID', 'Name']],
    left_on='Boss_ReportsTo',
    right_on='Emp ID',
    how='left',
    suffixes=('', '_BossBoss')
    )

    # Rename for clarity
    merged_with_bossboss.rename(columns={
    'Emp ID_BossBoss': 'BossBoss_ID',
    'Name_BossBoss': 'BossBoss_Name'
    }, inplace=True)

    # Select final useful columns
    final_result = merged_with_bossboss[[
    'Emp ID', 'Name', 'Boss_ID', 'Boss_Name', 'BossBoss_ID', 'BossBoss_Name'
    ]]

    # Replace NaN/None with empty strings
    final_result = final_result.fillna('')

    final_result

    Q3)
    # Load source data
    df = xl("Data[#All]", headers=True)

    # Drop unnecessary columns safely
    cols_to_drop = ['# Reports to my boss', "Boss'Boss", 'Chain']
    df = df.drop(columns=cols_to_drop, errors='ignore')

    # Ensure Emp ID and Reports to are strings; replace 'None' with blank
    df['Emp ID'] = df['Emp ID'].astype(str)
    df['Reports to'] = df['Reports to'].astype(str).replace('None', '')

    # Create lookup dictionary for fast access
    emp_dict = df.set_index('Emp ID')[['Name', 'Reports to']].to_dict(orient='index')

    def build_reporting_chain(emp_id, max_depth=50):
    chain = []
    visited = set()
    current = emp_id

    for _ in range(max_depth):
    if not current or current in visited:
    break
    visited.add(current)
    rec = emp_dict.get(current)
    if rec is None:
    break
    chain.append(rec['Name'])
    current = rec['Reports to']
    return " -> ".join(chain)

    # Apply the function to build reporting chains
    df['Reporting Chain'] = df['Emp ID'].apply(build_reporting_chain)

    # Sort dataframe by Emp ID ascending
    df = df.sort_values('Emp ID').reset_index(drop=True)

  18. Martin says:

    1)
    =COUNTIF(staff[Reports to],staff[@[Reports to]])

    2)
    =XLOOKUP(staff[@[Reports to]],staff[Emp ID],staff[Reports to],"",0)

    3)
    =IFERROR(TEXTBEFORE(staff[@Name]," ") & " ?? " & LET(
    Chain1,XLOOKUP(staff[@[Reports to]],staff[Emp ID],staff[[Emp ID]:[Reports to]],"",0),
    Chain2,XLOOKUP(INDEX(Chain1,,3),staff[Emp ID],staff[[Emp ID]:[Reports to]],"",0),
    Chain3,XLOOKUP(INDEX(Chain2,,3),staff[Emp ID],staff[[Emp ID]:[Reports to]],"",0),
    Chain4,XLOOKUP(INDEX(Chain3,,3),staff[Emp ID],staff[[Emp ID]:[Reports to]],"",0),
    Chain5,XLOOKUP(INDEX(Chain4,,3),staff[Emp ID],staff[[Emp ID]:[Reports to]],"",0),
    Chain6,XLOOKUP(INDEX(Chain5,,3),staff[Emp ID],staff[[Emp ID]:[Reports to]],"",0),
    TEXTBEFORE(INDEX(TEXTBEFORE(Chain1," ") & " ?? " &
    IFERROR(TEXTBEFORE(Chain2," ") & " ?? ","") &
    IFERROR(TEXTBEFORE(Chain3," ") & " ?? ","") &
    IFERROR(TEXTBEFORE(Chain4," ") & " ?? ","") &
    IFERROR(TEXTBEFORE(Chain5," ") & " ?? ","") &
    IFERROR(TEXTBEFORE(Chain6," ") & " ?? ",""),,2)," ?? ",-1)),"")

  19. Andy says:

    My answer is by Query. I am from Slovakia and my excell is set up in slovak language (I love Power Query becouse I cannot use any program language such as Pyhon, DAX...whatever, just a little bit and with query I solved so many tasks).

    let
    Zdroj = staff,
    #"Odstránené ostatné st?pce" = Table.SelectColumns(Zdroj,{"Emp ID", "Name", "Reports to"}),
    #"Zlú?ené dotazy" = Table.NestedJoin(#"Odstránené ostatné st?pce", {"Reports to"}, group, {"Reports to"}, "boss_1", JoinKind.LeftOuter),
    #"Rozbalené boss_1" = Table.ExpandTableColumn(#"Zlú?ené dotazy", "boss_1", {"# of reports to my boss"}, {"# of reports to my boss"}),
    #"Zlú?ené dotazy1" = Table.NestedJoin(#"Rozbalené boss_1", {"Reports to"}, ID_list, {"Emp ID"}, "bosses_boss", JoinKind.LeftOuter),
    #"Rozbalené bosses_boss" = Table.ExpandTableColumn(#"Zlú?ené dotazy1", "bosses_boss", {"Emp ID", "Name", "Reports to"}, {"Emp ID.1", "Name.1", "Reports to.1"}),
    #"Premenované st?pce" = Table.RenameColumns(#"Rozbalené bosses_boss",{{"Reports to.1", "bosses_boss"}}),
    #"Zlú?ené dotazy2" = Table.NestedJoin(#"Premenované st?pce", {"bosses_boss"}, ID_list, {"Emp ID"}, "bosses_boss.1", JoinKind.LeftOuter),
    #"Rozbalené bosses_boss.1" = Table.ExpandTableColumn(#"Zlú?ené dotazy2", "bosses_boss.1", {"Name", "Reports to"}, {"Name.2", "Reports to.1"}),
    #"Odstránené st?pce" = Table.RemoveColumns(#"Rozbalené bosses_boss.1",{"Emp ID.1"}),
    #"Extrahoval sa text pred odde?ova?om" = Table.TransformColumns(#"Odstránené st?pce", {{"Name.1", each Text.BeforeDelimiter(_, " "), type text}}),
    #"Zlú?ené dotazy3" = Table.NestedJoin(#"Extrahoval sa text pred odde?ova?om", {"Reports to.1"}, ID_list, {"Emp ID"}, "bosses_boss.1", JoinKind.LeftOuter),
    #"Rozbalené bosses_boss.2" = Table.ExpandTableColumn(#"Zlú?ené dotazy3", "bosses_boss.1", {"Name", "Reports to"}, {"bosses_boss.1.Name", "bosses_boss.1.Reports to"}),
    #"Premenované st?pce1" = Table.RenameColumns(#"Rozbalené bosses_boss.2",{{"Name.1", "chain_2"}, {"Name.2", "chain_3"}}),
    #"Extrahoval sa text pred odde?ova?om1" = Table.TransformColumns(#"Premenované st?pce1", {{"chain_3", each Text.BeforeDelimiter(_, " "), type text}}),
    #"Premenované st?pce2" = Table.RenameColumns(#"Extrahoval sa text pred odde?ova?om1",{{"bosses_boss.1.Name", "chain_4"}}),
    #"Extrahoval sa text pred odde?ova?om2" = Table.TransformColumns(#"Premenované st?pce2", {{"chain_4", each Text.BeforeDelimiter(_, " "), type text}}),
    #"Zlú?ené dotazy4" = Table.NestedJoin(#"Extrahoval sa text pred odde?ova?om2", {"bosses_boss.1.Reports to"}, ID_list, {"Emp ID"}, "bosses_boss.1", JoinKind.LeftOuter),
    #"Rozbalené bosses_boss.3" = Table.ExpandTableColumn(#"Zlú?ené dotazy4", "bosses_boss.1", {"Name", "Reports to"}, {"bosses_boss.1.Name", "bosses_boss.1.Reports to.1"}),
    #"Premenované st?pce3" = Table.RenameColumns(#"Rozbalené bosses_boss.3",{{"bosses_boss.1.Name", "chain_5"}}),
    #"Zlú?ené dotazy5" = Table.NestedJoin(#"Premenované st?pce3", {"bosses_boss.1.Reports to.1"}, ID_list, {"Emp ID"}, "bosses_boss.1", JoinKind.LeftOuter),
    #"Rozbalené bosses_boss.4" = Table.ExpandTableColumn(#"Zlú?ené dotazy5", "bosses_boss.1", {"Name", "Reports to"}, {"bosses_boss.1.Name", "bosses_boss.1.Reports to.2"}),
    #"Premenované st?pce4" = Table.RenameColumns(#"Rozbalené bosses_boss.4",{{"bosses_boss.1.Name", "chain_6"}}),
    #"Odstránené ostatné st?pce1" = Table.SelectColumns(#"Premenované st?pce4",{"Emp ID", "Name", "Reports to", "# of reports to my boss", "chain_2", "bosses_boss", "chain_3", "chain_4", "chain_5", "chain_6"}),
    #"Duplicitný st?pec" = Table.DuplicateColumn(#"Odstránené ostatné st?pce1", "Name", "Name – kópia"),
    #"Premenované st?pce5" = Table.RenameColumns(#"Duplicitný st?pec",{{"Name – kópia", "chain_1"}}),
    #"St?pce so zmeneným poradím" = Table.ReorderColumns(#"Premenované st?pce5",{"Emp ID", "Name", "Reports to", "# of reports to my boss", "chain_1", "chain_2", "chain_3", "chain_4", "chain_5", "chain_6"}),
    #"Extrahoval sa text pred odde?ova?om3" = Table.TransformColumns(#"St?pce so zmeneným poradím", {{"chain_1", each Text.BeforeDelimiter(_, " "), type text}}),
    #"Extrahoval sa text pred odde?ova?om4" = Table.TransformColumns(#"Extrahoval sa text pred odde?ova?om3", {{"chain_5", each Text.BeforeDelimiter(_, " "), type text}}),
    #"Extrahoval sa text pred odde?ova?om5" = Table.TransformColumns(#"Extrahoval sa text pred odde?ova?om4", {{"chain_6", each Text.BeforeDelimiter(_, " "), type text}}),
    #"St?pce so zmeneným poradím1" = Table.ReorderColumns(#"Extrahoval sa text pred odde?ova?om5",{"Emp ID", "Name", "Reports to", "# of reports to my boss", "bosses_boss", "chain_1", "chain_2", "chain_3", "chain_4", "chain_5", "chain_6"}),
    #"Zlú?ené st?pce" = Table.CombineColumns(#"St?pce so zmeneným poradím1",{"chain_1", "chain_2", "chain_3", "chain_4", "chain_5", "chain_6"},Combiner.CombineTextByDelimiter(">", QuoteStyle.None),"Zlú?ené"),
    #"Zoradené riadky" = Table.Sort(#"Zlú?ené st?pce",{{"Emp ID", Order.Ascending}}),
    #"Premenované st?pce6" = Table.RenameColumns(#"Zoradené riadky",{{"Zlú?ené", "chain"}})
    in
    #"Premenované st?pce6"

  20. K Baker says:

    Thanks for the challenge - I enjoyed it! These are the simplest approaches I could come up with. For Q3, the nested XLOOKUPS took a minute to figure out, but they work. : )

    Q1)
    =COUNTIF([Reports to],[@[Reports to]])

    Q2)
    =IFERROR(XLOOKUP([@[Reports to]],[Emp ID],[Reports to]),"")

    Q3)
    =CONCATENATE(
    TEXTBEFORE([@Name]," "),
    IFERROR("->"&XLOOKUP([@[Reports to]],[Emp ID],TEXTBEFORE([Name]," ")),""),
    IFERROR("->"&XLOOKUP([@[Boss''Boss]],[Emp ID],TEXTBEFORE([Name]," ")),""),
    IFERROR("->"&XLOOKUP((XLOOKUP([@[Boss''Boss]],[Emp ID],[Reports to])),[Emp ID],TEXTBEFORE([Name]," ")),""),
    IFERROR("->"&XLOOKUP((XLOOKUP((XLOOKUP([@[Boss''Boss]],[Emp ID],[Reports to])),[Emp ID],[Reports to])),[Emp ID],TEXTBEFORE([Name]," ")),""),
    IFERROR("->"&XLOOKUP((XLOOKUP((XLOOKUP((XLOOKUP([@[Boss''Boss]],[Emp ID],[Reports to])),[Emp ID],[Reports to])),[Emp ID],[Reports to])),[Emp ID],TEXTBEFORE([Name]," ")),"")
    )

  21. Paridhi says:

    2.
    =INDEX(staff[Boss ID],MATCH(INDEX(staff[Boss ID],MATCH("TO-0021",staff[EmpID],0)),staff[EmpID]))

  22. Astley says:

    1) SELECT b.EmpName AS Boss, COUNT(*) AS Subordinates
    FROM Employee e
    JOIN Employee b ON b.EmpID = e.MgrID
    WHERE b.EmpID = 'TO-0002'
    GROUP BY b.EmpID, b.EmpName;

    2) WITH RecursiveBossChain AS (
    SELECT employee_id, manager_id
    FROM Employee
    WHERE employee_id = 'TO-0021'

    UNION ALL

    SELECT e.employee_id, e.manager_id
    FROM Employee e
    JOIN RecursiveBossChain r ON e.employee_id = r.manager_id
    )
    SELECT manager_id
    FROM RecursiveBossChain
    WHERE employee_id = 'TO-0002';

    3) WITH RECURSIVE EmployeeHierarchy AS (
    -- Start with the employee
    SELECT EmpID, EmpName, MgrID, CAST(EmpName AS VARCHAR(1000)) AS Chain
    FROM Employee
    WHERE EmpID = 'TO-0021'

    UNION ALL

    -- Recursively climb up
    SELECT e.EmpID, e.EmpName, e.MgrID, eh.Chain || ' -> ' || e.EmpName
    FROM Employee e
    JOIN EmployeeHierarchy eh ON e.EmpID = eh.MgrID
    )
    SELECT Chain
    FROM EmployeeHierarchy
    WHERE MgrID IS NULL; -- Optionally stop at the top

  23. Karolina says:

    Thank you for the challenge. It gave me a lot of joy and would like to know a more effective solution for number 3:-). What would I need to add to know how many managers variables to create, without any helper column?

    1) =COUNTIFS([Reports to];[@[Reports to]])

    2) =IF(XLOOKUP(XLOOKUP([@[Reports to]];[Emp ID];[Name];"");[Name];[Reports to];"")=0;"";XLOOKUP(XLOOKUP([@[Reports to]];[Emp ID];[Name];"");[Name];[Reports to];""))

    3) =LET(
    empID;staff[Emp ID];
    Name;staff[Name];
    reportsTo;staff[Reports to];
    firstNames;TEXTBEFORE(Name;" ");
    managers;XLOOKUP(FILTER(empID;ISNUMBER(XMATCH(empID;reportsTo));"");empID;Name;"");
    managersID;XLOOKUP(managers;Name;empID;"");
    manNames;TEXTBEFORE(managers;" ");
    managers2;IF(XLOOKUP(managers;Name;reportsTo;"")=0;"";XLOOKUP(managers;Name;reportsTo;""));
    man2Names;IF(managers2="";"";TEXTBEFORE(XLOOKUP(managers2;empID;Name);" "));
    managers3;IF(XLOOKUP(managers2;empID;reportsTo;"")=0;"";XLOOKUP(managers2;empID;reportsTo;""));
    man3Names;IF(managers3="";"";TEXTBEFORE(XLOOKUP(managers3;empID;Name);" "));
    managers4;IF(XLOOKUP(managers3;empID;reportsTo;"")=0;"";XLOOKUP(managers3;empID;reportsTo;""));
    man4Names;IF(managers4="";"";TEXTBEFORE(XLOOKUP(managers4;empID;Name);" "));
    managers5;IF(XLOOKUP(managers4;empID;reportsTo;"")=0;"";XLOOKUP(managers4;empID;reportsTo;""));
    man5Names;IF(managers5="";"";TEXTBEFORE(XLOOKUP(managers5;empID;Name);" "));
    chain;HSTACK(manNames;man2Names;man3Names;man4Names;man5Names);
    lookup;BYROW(chain;LAMBDA(row;TEXTJOIN("->";TRUE;row)));
    IF(reportsTo="";firstNames;firstNames&"->"&XLOOKUP(reportsTo;managersID;lookup))
    )

  24. Javier says:

    Question 1
    COUNTIF([Reports to],[@[Reports to]])

    Question 2:
    LET(
    BossesBossID, XLOOKUP(
    XLOOKUP([@[Emp ID]], [Emp ID], [Reports to], "", 0),
    [Emp ID],
    [Reports to],
    "No upper boss",
    0
    ),
    IF(BossesBossID = 0, "No upper boss", BossesBossID)
    )

    Question 3:
    LET(
    StartID, [@[Emp ID]],
    IdRange, [Emp ID],
    NameRange, [Name],
    BossRange, [Reports to],

    FirstName, LAMBDA(text, LEFT(text, FIND(" ", text & " ") - 1)),

    Name1, FirstName(XLOOKUP(StartID, IdRange, NameRange, "Unknown")),
    BossID1, XLOOKUP(StartID, IdRange, BossRange, ""),

    Name2, IF(BossID1="", "", FirstName(XLOOKUP(BossID1, IdRange, NameRange, "."))),
    BossID2, IF(BossID1="", "", XLOOKUP(BossID1, IdRange, BossRange, "")),

    Name3, IF(BossID2="", "", FirstName(XLOOKUP(BossID2, IdRange, NameRange, "."))),
    BossID3, IF(BossID2="", "", XLOOKUP(BossID2, IdRange, BossRange, "")),

    Name4, IF(BossID3="", "", FirstName(XLOOKUP(BossID3, IdRange, NameRange, "."))),
    BossID4, IF(BossID3="", "", XLOOKUP(BossID3, IdRange, BossRange, "")),

    Name5, IF(BossID4="", "", FirstName(XLOOKUP(BossID4, IdRange, NameRange, "."))),
    BossID5, IF(BossID4="", "", XLOOKUP(BossID4, IdRange, BossRange, "")),

    Name6, IF(BossID5="", "", FirstName(XLOOKUP(BossID5, IdRange, NameRange, "."))),

    CompleteChain, TEXTJOIN("->", TRUE, Name1, Name2, Name3, Name4, Name5, Name6),

    IF(
    RIGHT(CompleteChain, 2) = "Jo",
    "",
    CompleteChain
    )
    )

    Thankk you for this challenge.

    Javier

  25. Philip Wiest says:

    Hey Chandoo...
    I think one "Roadmap" ? to success is to use PATH() and PATHITEM() in DAX. I think I learned this from you somewhere on the "Road to Awesome".

  26. Sonali says:

    Hello, chandoo,
    I tried solving the problem in SQL. Here’s my approach for MySQL

    -- How many people are directly reporting to my boss?
    select e.reports_to,count(1) as direct_reports from staff_emp e
    where e.reports_to=(select reports_to from staff_emp where emp_id='TO-0021')
    group by e.reports_to;

    -- For a given employee ID (say TO-0021, Cathy Simms), find out their boss’s boss. The answer should be TO-0007, Michael Scott.
    -- t1=boss, t2=boss's boss
    select b2.emp_id,b2.name from staff_emp e
    join staff_emp b1 on e.reports_to=b1.emp_id
    join staff_emp b2 on b1.reports_to=b2.emp_id
    where e.emp_id='TO-0021';

    -- What is my reporting chain?
    select CONCAT(
    SUBSTRING_INDEX(e.name, ' ', 1), '->',
    SUBSTRING_INDEX(b1.name, ' ', 1), '->',
    SUBSTRING_INDEX(b2.name, ' ', 1), '->',
    SUBSTRING_INDEX(b3.name, ' ', 1), '->',
    SUBSTRING_INDEX(b4.name, ' ', 1)
    ) AS reporting_chain from staff_emp e
    join staff_emp b1 on e.reports_to=b1.emp_id
    join staff_emp b2 on b1.reports_to=b2.emp_id
    join staff_emp b3 on b2.reports_to=b3.emp_id
    join staff_emp b4 on b3.reports_to=b4.emp_id
    where e.emp_id='TO-0021';

    Please let me know your feedback or if you see any improvements I can make.
    Thank u for this challenge.

  27. Hello Chandoo
    Office 365 in French
    1) =NB.SI([Reports to];[@[Reports to]])
    2) =RECHERCHEX([@[Reports to]];[Emp ID];[Reports to];"")
    3) =reportingChain(TEXTE.AVANT(staff[@Name]; " ");staff[@[Emp ID]])
    With Lambda
    =LET(
    ReportsTo; RECHERCHEX(EmpID; staff[Emp ID]; staff[Reports to]; "");
    SI(
    ReportsTo = "";
    FirstName;
    FirstName & "->" &
    reportingChain(
    TEXTE.AVANT(RECHERCHEX(ReportsTo; staff[Emp ID]; staff[Name]); " ");
    ReportsTo
    )
    )
    )
    Best regards
    J-M

  28. SIMMI SAHAY says:

    I did it in Oracle SQL using self join

    SQL> select m.empname ,count(e.empid) from staff e,staff m where e.bossid = m.empid group by m.empname;

    EMPNAME COUNT(E.EMPID)
    -------------------- --------------
    Jim HalPert 2
    Pam bee 1
    Michal 4
    Dwight 1
    Creed 1
    Angela 1

    6 rows selected.

    SQL> select e.empid,e.empname,e.bossid,m.empname from staff e,staff m where e.bossid = m.empid;

    EMPID EMPNAME BOSSID EMPNAME
    ---------- -------------------- ---------- --------------------
    TO-0018 Andy TO-0002 Jim HalPert
    TO-0021 Cathy TO-0002 Jim HalPert
    TO-0009 Erin TO-0004 Pam bee
    TO-0002 Jim HalPert TO-0007 Michal
    TO-0015 Dwight TO-0007 Michal
    TO-0028 Creed TO-0007 Michal
    TO-0035 Angela TO-0007 Michal
    TO-0004 Pam bee TO-0015 Dwight
    TO-0031 Kelly TO-0028 Creed
    TO-0024 Keveen TO-0035 Angela

    SQL> select e.empid,e.empname,e.bossid,m.empname from staff e,staff m where e.bossid = m.empid(+);

    EMPID EMPNAME BOSSID EMPNAME
    ---------- -------------------- ---------- --------------------
    TO-0018 Andy TO-0002 Jim HalPert
    TO-0021 Cathy TO-0002 Jim HalPert
    TO-0009 Erin TO-0004 Pam bee
    TO-0002 Jim HalPert TO-0007 Michal
    TO-0015 Dwight TO-0007 Michal
    TO-0028 Creed TO-0007 Michal
    TO-0035 Angela TO-0007 Michal
    TO-0004 Pam bee TO-0015 Dwight
    TO-0031 Kelly TO-0028 Creed
    TO-0024 Keveen TO-0035 Angela
    TO-0007 Michal TO-0067

    EMPID EMPNAME BOSSID EMPNAME
    ---------- -------------------- ---------- --------------------
    TO-0010 Jan TO-0067
    TO-0030 Tobby TO-0067
    TO-0033 Robert TO-0067

    14 rows selected.

  29. Naveed Rasheed says:

    I want to use unique ID CNIC and want to find any employee complete data
    1- type campus show all data campus wise
    2- type department show all employees data
    3- Cadre name and department then show employees data
    4- like many other desired fields
    Solve my problems
    Note:- formulas apply only main query file

  30. Adekoyejo Dada says:

    I used my favorite data manipulation tool "Microsoft Excel" to get all the answers.
    Question 1 (Answer) =COUNTIF($D$4:$D$31,$D$11) - D4:D31 is the Boss ID column and D11 is the "0002" ID; CountIf function was used to count the number of times "0002" appears in that column.

    Question 2 (Answer) = =VLOOKUP($B$4,$B$3:$G$31,3,FALSE) - B4 is the lookup value i.e Employee ID, B3:G31 is the total table array, 3 is the index column number (Boss ID) column and False is for the exact match

    Question 3 (Answer) = =INDEX($C$4:$C$31, MATCH(B12, $B$4:$B$31, 0))
    & " -> " &
    INDEX($C$4:$C$31, MATCH(INDEX($D$4:$D$31, MATCH(B12, $B$4:$B$31, 0)), $B$4:$B$31, 0))
    & " -> " &
    INDEX($C$4:$C$31, MATCH(INDEX($D$4:$D$31, MATCH(INDEX(D4:D31, MATCH(B12, $B$4:$B$31, 0)), $B$4:$B$31, 0)), $B$4:$B$31, 0))
    & " -> " &
    INDEX($C$4:$C$31, MATCH(INDEX($D$4:$D$31, MATCH(INDEX($D$4:$D$31, MATCH(INDEX($D$4:$D$31, MATCH(B12, $B$4:$B$31, 0)), $B$4:$B$31, 0)), $B$4:$B$31, 0)), $B$4:$B$31, 0))
    & " -> " &
    INDEX($C$4:$C$31, MATCH(INDEX($D$4:$D$31, MATCH(INDEX($D$4:$D$31, MATCH(INDEX($D$4:$D$31, MATCH(INDEX($D$4:$D$31, MATCH(B12, $B$4:$B$31, 0)), $B$4:$B$31, 0)), $B$4:$B$31, 0)), $B$4:$B$31, 0)), $B$4:$B$31, 0))

    Explanation:
    The reporting chain formula works by repeatedly using the INDEX and MATCH functions to look up an employee’s name from their ID, then use that ID to find their manager’s ID, and then look up that manager’s name, repeating the process for as many levels as needed. First, MATCH(EmpID_to_find, EmpID, 0) finds the row position of the employee in the EmpID range, and INDEX(EmpName, that_row) returns their name. To get the manager, INDEX(MgrID, MATCH(EmpID_to_find, EmpID, 0)) retrieves the manager’s ID from the MgrID range, and wrapping that in another INDEX/MATCH pair returns the manager’s name. Nesting this process allows you to step upward in the hierarchy, finding the manager’s manager, and so on, each time feeding the found manager ID back into another INDEX/MATCH to get the next name. The results for each level are then concatenated using " -> " so the final output reads like “Cathy Simms -> Jim Halpert -> Michael Scott -> Jo Bennet.” The limitation is that each additional hierarchy level must be manually added to the formula, so for a fully dynamic solution, Power Query or VBA would be more efficient.

  31. Duncan Williamson says:

    With the data in the range A5:C33 and with row 5 containing the column headers. Here are my formulas
    E4 contains a data validation cell with all Employee IDs in there
    For Q1 E6=XLOOKUP(E4,A6:A33,B6:B33)
    F6=XLOOKUP(E6,B6:B33,C6:C33)
    G6=COUNTIFS(C6:C33,F6)
    For Q2 and Q3 H7=LET(
    emp, E4,
    id, A6:A33,
    name, B6:B33,
    boss, C6:C33,
    steps, ROWS(id),
    ids, SCAN(emp, SEQUENCE(steps), LAMBDA(prev,_, XLOOKUP(prev, id, boss, ""))),
    chainIDs, FILTER(ids, ids""),
    XLOOKUP(chainIDs, id, name)
    )
    Final submission in E11=TEXTJOIN("->",,H7#)

    I will also add that I created a pivot table from the input data with Rows=Employee ID, Columns=Boss ID and Values=Count of Name
    Similarly, I created the following from the input data =PIVOTBY(relationships4[[#All],[Emp ID]],relationships4[[#All],[Boss ID]],relationships4[[#All],[Name]],COUNTA,3)

    • Naveed Rasheed says:

      I have a query file of university employees in sheet-1
      In sheet-2
      A1 is empty
      B1 is empty
      C1 is empty
      D1 is empty
      1- If I write only Campus name in cell A1 then campus wise employees data should be shown.
      2- If I write Campus name in cell A1 and Faculty name in cell B1 then campus wise & Department wise employees data should be shown
      3- If I write Campus name in cell A1 and Faculty name in cell B1 and then Department name in C1 then campus wise, Faculty wise & Department wise employees data should be shown
      4- so on

  32. Viktoras says:

    3. Used in-line recursive Lambda.

    =LAMBDA(empID,
    LET(getChainName,
    LAMBDA(self,empID,
    LET(bossName, TEXTBEFORE(XLOOKUP(empID, staff[Emp ID], staff[Name], "")," "),
    bossID, XLOOKUP(empID, staff[Emp ID], staff[Reports to], ""),
    IF( bossID = "", bossName, bossName & "->" & self(self, bossID) )
    )
    ),
    getChainName(getChainName, empID)
    )
    )("TO-0021")

  33. SUJITH N says:

    1 For a given employee ID (say TO-0021, Cathy Simms), how many people are directly reporting to their boss (TO-0002, Jim Halpert)?

    TO-0021
    3 =COUNTIF(B4:G30,VLOOKUP(J7,staff,3))

    2 The second question is for a given employee ID (say TO-0021, Cathy Simms), find out their boss’s boss. 

    TO-0021
    TO-0007 =VLOOKUP(VLOOKUP(J12,staff,3),staff,3)

    3 For a given employee (say TO-0021, Cathy Simms), print their entire reporting chain, delimited by the symbol ->.
    I don't know ,I have EXCEL-2019

  34. Bhanu says:

    -- 1) How many people are directly reporting to my boss?
    /* select
    bossid,
    count(distinct empid) as reportees_num
    from employees
    group by 1
    order by 1
    */
    -- 2) Who is my boss’s boss?
    /* select e.empid,
    coalesce(boss2.name,"not mentioned") as bosses_boss
    from employees e
    join employees boss1
    on e.bossid = boss1.empid
    join employees boss2
    on boss1.bossid = boss2.empid
    where e.empid = 'TO-0021'
    order by 1
    */

    -- 3) to find the full reporting chain for a given employee.

    /*
    WITH RECURSIVE reporting_chain AS (
    -- The Anchor Member: This is the starting point of the recursion.
    -- It selects the employee with EmpID 'TO-0021' and initializes
    -- the 'Chain' column with their name.
    SELECT
    EmpID,
    Name,
    BossID,
    CAST(Name AS VARCHAR(255)) AS Chain
    FROM
    employees
    WHERE
    EmpID = 'TO-0021'

    -- The UNION ALL operator combines the anchor member with the
    -- recursive member.
    UNION ALL

    -- The Recursive Member: This part joins the CTE with the
    -- 'employees' table to find the next person up the chain.
    SELECT
    e.EmpID,
    e.Name,
    e.BossID,
    CAST(rc.Chain || '->' || e.Name AS VARCHAR(255)) AS Chain
    FROM
    employees AS e
    INNER JOIN
    reporting_chain AS rc ON e.EmpID = rc.BossID
    )

    -- The final SELECT statement retrieves the full reporting chain.
    -- We order by the length of the chain to get the final, longest chain.
    SELECT
    Chain
    FROM
    reporting_chain
    ORDER BY
    LENGTH(Chain) DESC
    LIMIT 1;
    */

  35. Alan Olrog says:

    1) =COUNTIF([Reports to],[@[Reports to]])
    2) =LET(
    Boss1No,[@[Reports to]],
    Boss1Name,XLOOKUP(Boss1No,[Emp ID],[Name],""),

    Boss1Name
    )
    3) I wanted to do this in Excel. I knew a general algorithm would need an iterative solution with the exit condition of Boss = "". Recursion is probably the best solution: easy in VBA but I didn't know how to do it in Excel. I suspected LAMBDA would be involved, which the first comment confirms.

    I was only recently introduced to LET so this is my inelegant solution. I assumed 7 nodes max., as suggested. An interesting exercise!

    =LET(
    EmpFirstName, TEXTBEFORE([@Name]," ",,,,""),
    Boss1No,[@[Reports to]],
    Boss1FirstName,TEXTBEFORE(XLOOKUP(Boss1No,[Emp ID],[Name],"")," ",,,,""),

    Boss2No,XLOOKUP(Boss1No,[Emp ID],[Reports to],""),
    Boss2FirstName,TEXTBEFORE(XLOOKUP(Boss2No,[Emp ID],[Name],"")," ",,,,""),

    Boss3No,XLOOKUP(Boss2No,[Emp ID],[Reports to],""),
    Boss3FirstName,TEXTBEFORE(XLOOKUP(Boss3No,[Emp ID],[Name],"")," ",,,,""),

    Boss4No,XLOOKUP(Boss3No,[Emp ID],[Reports to],""),
    Boss4FirstName,TEXTBEFORE(XLOOKUP(Boss4No,[Emp ID],[Name],"")," ",,,,""),

    Boss5No,XLOOKUP(Boss4No,[Emp ID],[Reports to],""),
    Boss5FirstName,TEXTBEFORE(XLOOKUP(Boss5No,[Emp ID],[Name],"")," ",,,,""),

    Boss6No,XLOOKUP(Boss5No,[Emp ID],[Reports to],""),
    Boss6FirstName,TEXTBEFORE(XLOOKUP(Boss6No,[Emp ID],[Name],"")," ",,,,""),

    DeLim, "->",

    IF(Boss1FirstName="", EmpFirstName,
    IF(Boss2FirstName="",CONCAT(EmpFirstName, DeLim, Boss1FirstName),
    IF(Boss3FirstName="", CONCAT(EmpFirstName, DeLim, Boss1FirstName, DeLim, Boss2FirstName),
    IF(Boss4FirstName="", CONCAT(EmpFirstName, DeLim, Boss1FirstName, DeLim, Boss2FirstName, DeLim, Boss3FirstName),
    IF(Boss5FirstName="", CONCAT(EmpFirstName, DeLim, Boss1FirstName, DeLim, Boss2FirstName, DeLim, Boss3FirstName, DeLim, Boss4FirstName),
    IF(Boss6FirstName="", CONCAT(EmpFirstName, DeLim, Boss1FirstName, DeLim, Boss2FirstName, DeLim, Boss3FirstName, DeLim, Boss4FirstName, DeLim, Boss5FirstName),
    CONCAT(EmpFirstName, DeLim, Boss1FirstName, DeLim, Boss2FirstName, DeLim, Boss3FirstName, DeLim, Boss4FirstName, DeLim, Boss5FirstName, DeLim, Boss6FirstName)
    )
    )
    )
    )
    )
    )
    )

  36. Rajeev Shah says:

    1
    =TEXTJOIN(",",TRUE,FILTER(B:B,D:D=D12)) , =COUNTIF(D:D,D12)

    2 =TEXTJOIN(",",TRUE,FILTER(D:D,B:B=D12))

    3 =TEXTJOIN(",",TRUE,FILTER($D:$D,$B:$B=B12))
    TEXTJOIN(",",TRUE,FILTER($D:$D,$B:$B=L18))
    TEXTJOIN(",",TRUE,FILTER($D:$D,$B:$B=L19))
    TEXTJOIN(",",TRUE,FILTER($D:$D,$B:$B=L20))
    C12&"->"&K18&"->"&K19&"->"&K20&"->"&K21

  37. Wainers says:

    1. How many report to my boss
    =COUNTIFS([Reports to],[@[Reports to]])

    2. Whose my boss's boss
    =XLOOKUP([@[Reports to]],[Emp ID],[Reports to])

    3. Whose in my boss-chain
    =LET(
    empID, G16,
    empName, XLOOKUP(empID, data[Emp ID], data[Name]),
    maxLevel, G15,
    TEXTJOIN(
    ">", TRUE,
    VSTACK(
    empName,
    BYROW(
    SCAN(
    empID,
    SEQUENCE(maxLevel),
    LAMBDA(acc, val, XLOOKUP(acc, data[Emp ID], data[Reports to], "") ) ),
    LAMBDA(x, XLOOKUP(x, data[Emp ID], data[Name], "") ) ) ) ) )

  38. Bapa Ali says:

    1.let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Grouped Rows" = Table.Group(Source, {"Reports to"}, {{"Count", each Table.RowCount(_), Int64.Type}}),
    #"Sorted Rows" = Table.Sort(#"Grouped Rows",{{"Reports to", Order.Ascending}}),
    #"Merged Queries" = Table.NestedJoin(#"Sorted Rows", {"Reports to"}, #"Empp ID", {"Emp ID"}, "Empp ID", JoinKind.LeftOuter),
    #"Expanded Empp ID" = Table.ExpandTableColumn(#"Merged Queries", "Empp ID", {"Name"}, {"Name"}),
    #"Reordered Columns" = Table.ReorderColumns(#"Expanded Empp ID",{"Name", "Reports to", "Count"}),
    #"Sorted Rows1" = Table.Sort(#"Reordered Columns",{{"Reports to", Order.Ascending}}),
    #"Added Custom" = Table.AddColumn(#"Sorted Rows1", "Job", each "Boss"),
    #"Reordered Columns1" = Table.ReorderColumns(#"Added Custom",{"Reports to", "Name", "Count", "Job"}),
    #"Filtered Rows" = Table.SelectRows(#"Reordered Columns1", each ([Name] null))
    in
    #"Filtered Rows"

    2.let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Removed Columns" = Table.RemoveColumns(Source,{"Reports to"}),
    #"Merged Queries" = Table.NestedJoin(#"Removed Columns", {"Emp ID"}, #"ID BOSS (how many Answer)", {"Reports to"}, "ID BOSS", JoinKind.LeftOuter),
    #"Expanded ID BOSS" = Table.ExpandTableColumn(#"Merged Queries", "ID BOSS", {"Job"}, {"Job"}),
    #"Replaced Value" = Table.ReplaceValue(#"Expanded ID BOSS",null,"Employee",Replacer.ReplaceValue,{"Job"}),
    #"Sorted Rows" = Table.Sort(#"Replaced Value",{{"Job", Order.Descending}}),
    #"Filtered Rows" = Table.SelectRows(#"Sorted Rows", each ([Job] = "Employee")),
    #"Sorted Rows1" = Table.Sort(#"Filtered Rows",{{"Emp ID", Order.Ascending}}),
    #"Merged Queries1" = Table.NestedJoin(#"Sorted Rows1", {"Emp ID"}, #"Empp ID report to boss", {"Emp ID"}, "Empp ID report to boss", JoinKind.LeftOuter),
    #"Expanded Empp ID report to boss" = Table.ExpandTableColumn(#"Merged Queries1", "Empp ID report to boss", {"Reports to"}, {"Reports to"}),
    #"Merged Queries2" = Table.NestedJoin(#"Expanded Empp ID report to boss", {"Reports to"}, #"Empp ID", {"Emp ID"}, "Empp ID", JoinKind.LeftOuter),
    #"Expanded Empp ID" = Table.ExpandTableColumn(#"Merged Queries2", "Empp ID", {"Name"}, {"Name.1"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded Empp ID",{{"Name.1", "Boss 1"}}),
    #"Merged Queries3" = Table.NestedJoin(#"Renamed Columns", {"Reports to"}, #"Empp ID report to boss", {"Emp ID"}, "Empp ID report to boss", JoinKind.LeftOuter),
    #"Expanded Empp ID report to boss1" = Table.ExpandTableColumn(#"Merged Queries3", "Empp ID report to boss", {"Reports to"}, {"Reports to.1"}),
    #"Renamed Columns1" = Table.RenameColumns(#"Expanded Empp ID report to boss1",{{"Reports to.1", "Reports to 1"}}),
    #"Merged Queries4" = Table.NestedJoin(#"Renamed Columns1", {"Reports to 1"}, #"Empp ID", {"Emp ID"}, "Empp ID", JoinKind.LeftOuter),
    #"Expanded Empp ID1" = Table.ExpandTableColumn(#"Merged Queries4", "Empp ID", {"Name"}, {"Name.1"}),
    #"Renamed Columns2" = Table.RenameColumns(#"Expanded Empp ID1",{{"Name.1", "Boss 2"}, {"Reports to 1", "Reports to 2"}}),
    #"Merged Queries5" = Table.NestedJoin(#"Renamed Columns2", {"Reports to 2"}, #"Empp ID report to boss", {"Emp ID"}, "Empp ID report to boss", JoinKind.LeftOuter),
    #"Expanded Empp ID report to boss2" = Table.ExpandTableColumn(#"Merged Queries5", "Empp ID report to boss", {"Reports to"}, {"Reports to.1"}),
    #"Renamed Columns3" = Table.RenameColumns(#"Expanded Empp ID report to boss2",{{"Reports to.1", "Reports to 3"}}),
    #"Merged Queries6" = Table.NestedJoin(#"Renamed Columns3", {"Reports to 3"}, #"Empp ID", {"Emp ID"}, "Empp ID", JoinKind.LeftOuter),
    #"Expanded Empp ID2" = Table.ExpandTableColumn(#"Merged Queries6", "Empp ID", {"Name"}, {"Name.1"}),
    #"Renamed Columns4" = Table.RenameColumns(#"Expanded Empp ID2",{{"Name.1", "Boss 3"}, {"Reports to", "ID Boss 1"}, {"Reports to 2", "ID Boss 2"}, {"Reports to 3", "ID Boss 3"}}),
    #"Removed Other Columns" = Table.SelectColumns(#"Renamed Columns4",{"Emp ID", "Name", "ID Boss 1", "Boss 1"}),
    #"Renamed Columns5" = Table.RenameColumns(#"Removed Other Columns",{{"ID Boss 1", "ID Boss"}, {"Boss 1", "Boss'Boss"}}),
    #"Appended Query" = Table.Combine({#"Renamed Columns5", #"Boss'Boss 2"})
    in
    #"Appended Query"

    3.let
    Source = Table.Combine({#"Chain emp", #"Chain boss"}),
    #"Sorted Rows" = Table.Sort(Source,{{"ID Emp", Order.Ascending}})
    in
    #"Sorted Rows"

  39. Shinjini says:

    CREATE TABLE employee (
    employee_id VARCHAR(10) PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    boss_id VARCHAR(10) NULL
    );

    INSERT INTO employee (employee_id, name, boss_id) VALUES
    ('TO-0002', 'Jim Halpert', 'TO-0007'),
    ('TO-0004', 'Pam Beesly', 'TO-0015'),
    ('TO-0007', 'Michael Scott', 'TO-0067'),
    ('TO-0009', 'Erin Hannon', 'TO-0004'),
    ('TO-0010', 'Jan Levinson', 'TO-0067'),
    ('TO-0013', 'Karen Filippelli', 'TO-0076'),
    ('TO-0015', 'Dwight Schrute', 'TO-0007'),
    ('TO-0018', 'Andy Bernard', 'TO-0002'),
    ('TO-0021', 'Cathy Simms', 'TO-0002'),
    ('TO-0024', 'Kevin Malone', 'TO-0035'),
    ('TO-0028', 'Creed Bratton', 'TO-0007'),
    ('TO-0030', 'Toby Flenderson', 'TO-0067'),
    ('TO-0031', 'Kelly Kapoor', 'TO-0028'),
    ('TO-0033', 'Robert California', 'TO-0067'),
    ('TO-0035', 'Angela Martin', 'TO-0007'),
    ('TO-0039', 'Stanley Hudson', 'TO-0007'),
    ('TO-0043', 'Ryan Howard', 'TO-0031'),
    ('TO-0046', 'Oscar Martinez', 'TO-0035'),
    ('TO-0050', 'Meredith Palmer', 'TO-0028'),
    ('TO-0051', 'Gabe Lewis', 'TO-0072'),
    ('TO-0055', 'Phyllis Vance', 'TO-0030'),
    ('TO-0059', 'Roy Anderson', 'TO-0004'),
    ('TO-0063', 'Nellie Bertram', 'TO-0030'),
    ('TO-0066', 'Darryl Philbin', 'TO-0039'),
    ('TO-0067', 'David Wallace', 'TO-0072'),
    ('TO-0068', 'Pete Miller', 'TO-0015'),
    ('TO-0072', 'Jo Bennet', NULL),
    ('TO-0076', 'Todd Parker', 'TO-0002');

    # Q1.

    SELECT COUNT(*) AS direct_reports_count
    FROM employee
    WHERE boss_id = (
    SELECT boss_id
    FROM employee
    WHERE employee_id = 'TO-0021'
    );

    # Q2.

    SELECT g.employee_id AS grandboss_id, g.name AS grandboss_name
    FROM employee e
    LEFT JOIN employee b ON b.employee_id = e.boss_id
    LEFT JOIN employee g ON g.employee_id = b.boss_id
    WHERE e.employee_id = 'TO-0021';

    # Q3.

    SELECT
    CONCAT(e.name, ' -> ', b1.name, ' -> ', b2.name, ' -> ', b3.name, ' -> ', b4.name) AS chain
    FROM employee e
    LEFT JOIN employee b1 ON b1.employee_id = e.boss_id
    LEFT JOIN employee b2 ON b2.employee_id = b1.boss_id
    LEFT JOIN employee b3 ON b3.employee_id = b2.boss_id
    LEFT JOIN employee b4 ON b4.employee_id = b3.boss_id
    WHERE e.employee_id = 'TO-0021';

  40. Clarence says:

    ===========================================
    Q1) How many people are directly reporting to my boss? (Score = 5pts)
    ===========================================

    import pandas as pd

    # Load the CSV created from the image (uploaded by user)
    csv_path = "/mnt/data/employees_from_image_clean.csv"
    df = pd.read_csv(csv_path)

    # Step 1: Employee info
    employee_id = "TO-0021"
    employee_name = df.loc[df["Emp ID"] == employee_id, "Name"].iloc[0]

    # Step 2: Find Boss ID and Name
    boss_id = df.loc[df["Emp ID"] == employee_id, "Boss ID"].iloc[0]
    boss_name = df.loc[df["Emp ID"] == boss_id, "Name"].iloc[0]

    # Step 3: Count how many report to that boss
    direct_reports = df[df["Boss ID"] == boss_id].shape[0]

    # Step 4: Human-friendly output
    output_message = (
    f"{employee_name} (ID: {employee_id}) reports to {boss_name} (ID: {boss_id}). "
    f"{boss_name} has {direct_reports} direct reports, including {employee_name}."
    )

    output_message

  41. Clarence says:

    ========================================
    ====== Q1) How many people are directly reporting ====
    ====== to my boss? (Score = 5pts) ====
    ========================================

    import pandas as pd

    # Load the CSV created from the image (uploaded by user)
    csv_path = "/mnt/data/employees_from_image_clean.csv"
    df = pd.read_csv(csv_path)

    # Step 1: Employee info
    employee_id = "TO-0021"
    employee_name = df.loc[df["Emp ID"] == employee_id, "Name"].iloc[0]

    # Step 2: Find Boss ID and Name
    boss_id = df.loc[df["Emp ID"] == employee_id, "Boss ID"].iloc[0]
    boss_name = df.loc[df["Emp ID"] == boss_id, "Name"].iloc[0]

    # Step 3: Count how many report to that boss
    direct_reports = df[df["Boss ID"] == boss_id].shape[0]

    # Step 4: Human-friendly output
    output_message = (
    f"{employee_name} (ID: {employee_id}) reports to {boss_name} (ID: {boss_id}). "
    f"{boss_name} has {direct_reports} direct reports, including {employee_name}."
    )

    output_message

  42. David N says:

    1. =ROWS(FILTER([Emp ID],[Reports to]=[@[Reports to]]))

    2. =LET(
    boss,XLOOKUP([@[Reports to]],[Emp ID],[Reports to],""),
    IF(LEN(boss)=0,"",boss)
    )

    3. =LET(
    custom,LAMBDA(ME,node,hier,
    IF(LEN(node)=0,
    hier,
    ME(ME,
    XLOOKUP(node,[Emp ID],[Reports to]),
    VSTACK(hier,node)
    ))),
    chain,custom(custom,[@[Reports to]],[@[Emp ID]]),
    TEXTJOIN("->",,TEXTBEFORE(XLOOKUP(chain,[Emp ID],[Name])," "))
    )

    And to have the reporting chain go top-down instead of bottom-up, just switch the two pieces of the stack: VSTACK(node,hier).

  43. Dr Peter Bartholomew says:

    I started by writing Lambda functions BOSS? and STAFF? to look up the boss and list of staff respectively in the reporting chain centred upon the individual. I tried to avoid overt use of the primary and foreign keys and work with the name field.

    BOSS? = LAMBDA(name, [dummy],
    LET(
    boss?sID, XLOOKUP(name, empName, boss, ""),
    bossName, IF(boss?sID"", XLOOKUP(boss?sID, empID, empName, ""), ""),
    IF(dummy, bossName, boss?sID)
    )
    );

    That gets tricky in that it has to account for there being not boss for the most senior staff member and, perversely, one of the questions expects an employee ID to be returned in place of a name.

    The function to return the boss's boss might be
    BOSS²? = LAMBDA(name,
    REDUCE(name, {1,0}, BOSS?)
    );

    Conversely, the function to return staff reporting to a given individual is a little more straightforward
    STAFF? = LAMBDA(name, [option],
    FILTER(empName, boss = XLOOKUP(name, empName, empID))
    );
    though is complicated by the need to return the count of staff members
    STAFFCOUNT? = LAMBDA(name, COUNTA(STAFF?(name)));

    One's boss's boss might be given by
    BOSS²? = LAMBDA(name,
    REDUCE(name, {1,0}, BOSS?)
    );
    or by simply nesting the BOSS? function.

    To return an entire reporting chain again requires recursion, here achieved with SCAN
    CHAIN? = LAMBDA(name,
    LET(
    reportingChain, VSTACK(name, SCAN(name, SEQUENCE(7), BOSS?)),
    firstNames, TOROW(TEXTBEFORE(reportingChain," ",1,,1," "), 3),
    returnString, TEXTJOIN("->", , firstNames),
    returnString
    )
    );

    Once that little lot is in place the dynamic array version of the solution requires worksheet formulae
    = MAP(BOSS?(staff[Name], 1), STAFFCOUNT?)
    = BOSS²?(staff[Name])
    = MAP(staff[Name], CHAIN?)

    As an alternative, one might use single cell array formulae within the Table
    = STAFFCOUNT?(BOSS?([@Name], 1))
    = BOSS²?([@Name])
    = CHAIN?([@Name])

    Perhaps this does not form the best introduction to an article on PowerQuery, but I tend only to switch platforms if essential.

  44. Enny says:

    I used Power Query. Loaded staff and duplicated it twice.
    First Query:
    let
    Source = Excel.CurrentWorkbook(){[Name="staff"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Emp ID", type text}, {"Name", type text}, {"Reports to", type text}, {"# Reports to my boss", type any}, {"Boss'Boss", type text}, {"Chain", type text}}),
    #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"# Reports to my boss", "Boss'Boss", "Chain"}),
    #"Extracted Text Before Delimiter" = Table.TransformColumns(#"Removed Columns", {{"Name", each Text.BeforeDelimiter(_, " "), type text}})
    in
    #"Extracted Text Before Delimiter"

    Second Query
    let
    Source = Excel.CurrentWorkbook(){[Name="staff"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Emp ID", type text}, {"Name", type text}, {"Reports to", type text}, {"# Reports to my boss", type any}, {"Boss'Boss", type text}, {"Chain", type text}}),
    #"Grouped Rows" = Table.Group(#"Changed Type", {"Reports to"}, {{"Number of reportees", each Table.RowCount(_), Int64.Type}})
    in
    #"Grouped Rows"

    Third Query
    let
    Source = Excel.CurrentWorkbook(){[Name="staff"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Emp ID", type text}, {"Name", type text}, {"Reports to", type text}, {"# Reports to my boss", type any}, {"Boss'Boss", type text}, {"Chain", type text}}),
    #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"# Reports to my boss", "Boss'Boss", "Chain"}),
    #"Extracted Text Before Delimiter" = Table.TransformColumns(#"Removed Columns", {{"Name", each Text.BeforeDelimiter(_, " "), type text}}),
    #"Merged Queries" = Table.NestedJoin(#"Extracted Text Before Delimiter", {"Reports to"}, staff, {"Emp ID"}, "staff", JoinKind.LeftOuter),
    #"Expanded staff" = Table.ExpandTableColumn(#"Merged Queries", "staff", {"Name", "Reports to"}, {"Name.1", "Reports to.1"}),
    #"Merged Queries1" = Table.NestedJoin(#"Expanded staff", {"Reports to.1"}, staff, {"Emp ID"}, "staff", JoinKind.LeftOuter),
    #"Expanded staff1" = Table.ExpandTableColumn(#"Merged Queries1", "staff", {"Name", "Reports to"}, {"Name.2", "Reports to.2"}),
    #"Merged Queries2" = Table.NestedJoin(#"Expanded staff1", {"Reports to.2"}, staff, {"Emp ID"}, "staff", JoinKind.LeftOuter),
    #"Expanded staff2" = Table.ExpandTableColumn(#"Merged Queries2", "staff", {"Name", "Reports to"}, {"Name.3", "Reports to.3"}),
    #"Merged Queries3" = Table.NestedJoin(#"Expanded staff2", {"Reports to.3"}, staff, {"Emp ID"}, "staff", JoinKind.LeftOuter),
    #"Expanded staff3" = Table.ExpandTableColumn(#"Merged Queries3", "staff", {"Name", "Reports to"}, {"Name.4", "Reports to.4"}),
    #"Merged Queries4" = Table.NestedJoin(#"Expanded staff3", {"Reports to.4"}, staff, {"Emp ID"}, "staff", JoinKind.LeftOuter),
    #"Expanded staff4" = Table.ExpandTableColumn(#"Merged Queries4", "staff", {"Name"}, {"Name.5"}),
    #"Inserted Merged Column" = Table.AddColumn(#"Expanded staff4", "Report Chain", each Text.Combine({[Name], [Name.1], [Name.2], [Name.3], [Name.4], [Name.5]}, "->"), type text),
    #"Removed Columns1" = Table.RemoveColumns(#"Inserted Merged Column",{"Name.1", "Name.2", "Reports to.2", "Name.3", "Reports to.3", "Name.4", "Reports to.4", "Name.5"}),
    #"Added Conditional Column" = Table.AddColumn(#"Removed Columns1", "Boss's Boss", each if [Reports to.1] = null then [Reports to] else [Reports to]),
    #"Removed Columns2" = Table.RemoveColumns(#"Added Conditional Column",{"Reports to.1"}),
    #"Merged Queries5" = Table.NestedJoin(#"Removed Columns2", {"Reports to"}, #"Number of Employees", {"Reports to"}, "Number of Employees", JoinKind.LeftOuter),
    #"Expanded Number of Employees" = Table.ExpandTableColumn(#"Merged Queries5", "Number of Employees", {"Number of reportees"}, {"Number of reportees"})
    in
    #"Expanded Number of Employees"

  45. Mancky says:

    Great test, especially for no. 3. For said question, there are multiple ways of getting to the answer, but the train of thought was to build a model that can be dynamic enough to handle huge and changing datasets. Hence the Power Query approach was undertaken. Here's my code:

    let
    Source = staff,
    #"Removed Columns" = Table.RemoveColumns(Source,{"# Reports to my boss", "Boss'Boss", "Chain"}),
    #"Merged Queries" = Table.NestedJoin(#"Removed Columns", {"Reports to"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff" = Table.ExpandTableColumn(#"Merged Queries", "dt_Staff", {"Reports to"}, {"Reports to.1"}),
    #"Merged Queries1" = Table.NestedJoin(#"Expanded dt_Staff", {"Reports to.1"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff1" = Table.ExpandTableColumn(#"Merged Queries1", "dt_Staff", {"Reports to"}, {"Reports to.2"}),
    #"Merged Queries2" = Table.NestedJoin(#"Expanded dt_Staff1", {"Reports to.2"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff2" = Table.ExpandTableColumn(#"Merged Queries2", "dt_Staff", {"Reports to"}, {"Reports to.3"}),
    #"Merged Queries3" = Table.NestedJoin(#"Expanded dt_Staff2", {"Reports to.3"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff3" = Table.ExpandTableColumn(#"Merged Queries3", "dt_Staff", {"Reports to"}, {"Reports to.4"}),
    #"Merged Queries4" = Table.NestedJoin(#"Expanded dt_Staff3", {"Reports to.4"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff4" = Table.ExpandTableColumn(#"Merged Queries4", "dt_Staff", {"Reports to"}, {"Reports to.5"}),
    #"Inserted Text Before Delimiter" = Table.AddColumn(#"Expanded dt_Staff4", "Name1", each Text.BeforeDelimiter([Name], " "), type text),
    #"Merged Queries5" = Table.NestedJoin(#"Inserted Text Before Delimiter", {"Reports to"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff5" = Table.ExpandTableColumn(#"Merged Queries5", "dt_Staff", {"First"}, {"First"}),
    #"Merged Queries6" = Table.NestedJoin(#"Expanded dt_Staff5", {"Reports to.1"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff6" = Table.ExpandTableColumn(#"Merged Queries6", "dt_Staff", {"First"}, {"First.1"}),
    #"Merged Queries7" = Table.NestedJoin(#"Expanded dt_Staff6", {"Reports to.2"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff7" = Table.ExpandTableColumn(#"Merged Queries7", "dt_Staff", {"First"}, {"First.2"}),
    #"Merged Queries8" = Table.NestedJoin(#"Expanded dt_Staff7", {"Reports to.3"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff8" = Table.ExpandTableColumn(#"Merged Queries8", "dt_Staff", {"First"}, {"First.3"}),
    #"Merged Queries10" = Table.NestedJoin(#"Expanded dt_Staff8", {"Reports to.4"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff10" = Table.ExpandTableColumn(#"Merged Queries10", "dt_Staff", {"First"}, {"First.4"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded dt_Staff10",{{"Reports to.1", "Rollup2"}, {"Reports to.2", "Rollup3"}, {"Reports to.3", "Rollup4"}, {"Reports to.4", "Rollup5"}, {"Reports to.5", "Rollup6"}, {"Reports to", "Rollup1"}, {"First", "Name2"}, {"First.1", "Name3"}, {"First.2", "Name4"}, {"First.3", "Name5"}, {"First.4", "Name6"}}),
    #"Merged Queries9" = Table.NestedJoin(#"Renamed Columns", {"Rollup6"}, dt_Staff, {"Emp ID"}, "dt_Staff", JoinKind.LeftOuter),
    #"Expanded dt_Staff9" = Table.ExpandTableColumn(#"Merged Queries9", "dt_Staff", {"First"}, {"First"}),
    #"Renamed Columns1" = Table.RenameColumns(#"Expanded dt_Staff9",{{"First", "Name7"}}),
    #"Replaced Value" = Table.ReplaceValue(#"Renamed Columns1",null,"",Replacer.ReplaceValue,{"Rollup1", "Rollup2", "Rollup3", "Rollup4", "Rollup5", "Rollup6", "Name1", "Name2", "Name3", "Name4", "Name5", "Name6","Name7"}),
    #"Added Conditional Column" = Table.AddColumn(#"Replaced Value", "C1", each if [Name1] = "" then "" else "->"),
    #"Added Conditional Column1" = Table.AddColumn(#"Added Conditional Column", "C2", each if [Name2] = "" then "" else "->"),
    #"Added Conditional Column2" = Table.AddColumn(#"Added Conditional Column1", "C3", each if [Name3] = "" then "" else "->"),
    #"Added Conditional Column3" = Table.AddColumn(#"Added Conditional Column2", "C4", each if [Name4] = "" then "" else "->"),
    #"Added Conditional Column4" = Table.AddColumn(#"Added Conditional Column3", "C5", each if [Name5] = "" then "" else "->"),
    #"Added Conditional Column5" = Table.AddColumn(#"Added Conditional Column4", "C6", each if [Name6] = "" then "" else "->"),
    #"Added Conditional Column6" = Table.AddColumn(#"Added Conditional Column5", "C7", each if [Name7] = "" then "" else "->"),
    #"Added Custom" = Table.AddColumn(#"Added Conditional Column6", "Combo", each [Name1]&[C2]&[Name2]&[C3]&[Name3]&[C4]&[Name4]&[C5]&[Name5]&[C6]&[Name6]),
    #"Removed Columns1" = Table.RemoveColumns(#"Added Custom",{"Rollup2", "Rollup3", "Rollup4", "Rollup5", "Rollup6", "Name1", "Name2", "Name3", "Name4", "Name5", "Name6", "C1", "C2", "C3", "C4", "C5", "C6", "Name7", "C7"})
    in
    #"Removed Columns1"

  46. Ruchismita says:

    I have shared my file in mail box.

  47. Sornakanth says:

    1. How many people are directly reporting to my boss?
    =COUNTIF([Reports to],[@[Reports to]])
    RESULT = 5, 2, 4, 2, 4, 1, 5, 3, 3, 2, 5, 4, 2, 4, 5, 5, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 0, 3

    2. Who is my boss’s boss?
    =INDEX([Reports to],MATCH([@[Reports to]],[Emp ID],0))
    RESULT = TO-0067, TO-0007, TO-0072, TO-0015, TO-0072, TO-0002, TO-0067, TO-0007, TO-0007, TO-0007, TO-0067, TO-0072, TO-0007, TO-0072, TO-0067, TO-0067, TO-0028, TO-0007, TO-0007, 0, TO-0067, TO-0015, TO-0067, TO-0007, 0, TO-0007, TO-0007

    3. What is my reporting chain?
    Reporting Level-1,
    =INDEX(staff[[Emp ID]:[Reports to]],MATCH(staff[@[Reports to]],staff[Emp ID],0),2)

    Reporting Level-2,
    =INDEX(staff[[Emp ID]:[Boss''Boss]],MATCH(staff[@[Boss''Boss]],staff[Emp ID],0),2)
    .
    .
    .
    Reporting Level-5

    Order of Employees by Reporting Levels (Full Names)
    =TEXTJOIN(", ",TRUE,IFERROR(C4,""),IFERROR(I4,""),IFERROR(J4,""),IFERROR(L4,""),IFERROR(N4,""),IFERROR(P4,""))

    Only First Name,
    =TEXTJOIN("->", TRUE, TEXTBEFORE(TRIM(TEXTSPLIT(R4, ",")), " "))
    RESULT =
    Jim->Michael->David->Jo
    Pam->Dwight->Michael->David->Jo
    Michael->David->Jo
    Erin->Pam->Dwight->Michael->David->Jo
    Jan->David->Jo
    Karen->Todd->Jim->Michael->David->Jo
    Dwight->Michael->David->Jo
    Andy->Jim->Michael->David->Jo
    Cathy->Jim->Michael->David->Jo
    Kevin->Angela->Michael->David->Jo
    Creed->Michael->David->Jo
    Toby->David->Jo
    Kelly->Creed->Michael->David->Jo
    Robert->David->Jo
    Angela->Michael->David->Jo
    Stanley->Michael->David->Jo
    Ryan->Kelly->Creed->Michael->David->Jo
    Oscar->Angela->Michael->David->Jo
    Meredith->Creed->Michael->David->Jo
    Gabe->Jo
    Phyllis->Toby->David->Jo
    Roy->Pam->Dwight->Michael->David->Jo
    Nellie->Toby->David->Jo
    Darryl->Stanley->Michael->David->Jo
    David->Jo
    Pete->Dwight->Michael->David->Jo
    Jo
    Todd->Jim->Michael->David->Jo

  48. Mark says:

    OK, first time using the site after my tutor recommended it.
    Havent quite hit 3 but heres my attempt

    1. =COUNTIF([Reports to],[@[Reports to]])
    2. =XLOOKUP([@[Reports to]],[Emp ID],[Reports to],"")

    have done the text split, and ended up limiting it to 5 but did
    3. =[@Name]&"->"
    &(XLOOKUP([@[Reports to]],[Emp ID],[Name],""))
    &"->"
    &(XLOOKUP(XLOOKUP([@[Reports to]],[Emp ID],[Reports to],""),[Emp ID],[Name],""))
    &"->"
    &(XLOOKUP(XLOOKUP(XLOOKUP([@[Reports to]],[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Name],""))
    &"->"
    &(XLOOKUP(XLOOKUP(XLOOKUP(XLOOKUP([@[Reports to]],[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Name],""))
    &"->"
    &(XLOOKUP(XLOOKUP(XLOOKUP(XLOOKUP(XLOOKUP([@[Reports to]],[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Reports to],""),[Emp ID],[Name],""))

    which kind of worked, but is kind of messy

  49. Bemuel says:

    Really enjoyed the challenge. Thanks for sharing.
    1. =COUNTIF([Reports to],[@[Reports to]]) in excel
    2. =XLOOKUP([@[Reports to]],[Emp ID], [Reports to]) in excel
    3. Created a named function with the formula below in google sheets:
    =LET(reports_to, XLOOKUP(XLOOKUP(name, name_range, report_range,""), id_range, name_range, ""),
    IF(OR(reports_to="", reports_to=name), LEFT(name,FIND(" ",name)), LEFT(name,FIND(" ",name)) & " -> " & RCHAIN(reports_to, id_range, name_range, report_range))
    )
    Executed the function as shown below:
    =RCHAIN(B4,staff[Emp ID],staff[Name],staff[Reports to])

  50. Tony F says:

    1. =COUNTIF(staff[Reports to],staff[@[Emp ID]]) -- 3

    2. =XLOOKUP(D4,staff[Emp ID],staff[Name]) -- Michael Scott

    3. =FILTER(staff[Name],staff[Reports to]=B4,"") --
    Andy Bernard
    Cathy Simms
    Todd Parker

  51. JuanJo says:

    Good challenges.
    Answers:
    1. How may people ar directly reportin to my boss? I used =CONTAR.SI.CONJUNTO($D$4:$D$31;$D4)
    2. Who is my Boss's? I used
    =SI(SI.ND(BUSCARV($D4;$B$4:$D$31;3;0);"")0;SI.ND(BUSCARV($D4;$B$4:$D$31;3;0);"");D4)
    3. What is my reportin chain? I used a LAMBDA Recursive function
    =LAMBDA(empID;rangoEmp;rangoBoss;rangoName;
    LET(
    idx; COINCIDIR(empID; rangoEmp; 0);
    padre; INDICE(rangoBoss; idx);
    name; INDICE(rangoName; idx);
    apodo; IZQUIERDA(name;HALLAR(" ";name)-1);
    resultado; SI(
    O(Boss = ""; ESBLANCO(Boss));
    empID & " " & apodo;
    empID & " " & apodo & " ? " & BuscarCadena(Boss; rangoEmp; rangoBoss; rangoName)
    );
    resultado
    )
    )

Leave a Reply