Pair the staff Swipes. The earliest swipe is considered as Time IN and the Last swipe as Time OUT Capture the absent staff, too. Dynamic array function allowed, but Extra marks for Legacy solutions or PowerQuery Solutions.
📌 Challenge Details and Links
Challenge Number: 73
Challenge Difficulty: ⭐⭐
📥Download Sample File
📥Link to the solutions on LinkedIn
Solving the challenge of Pair Swipes with Power Query
Power Query solution 1 for Pair Swipes, proposed by Zoran Milokanović:
let
Source = Excel.CurrentWorkbook(){[Name = "Input"]}[Content],
P = List.Transform(Table.ToColumns(Source), List.Distinct),
_ = Table.Sort(
Table.Group(
Source
& Table.FromRows(
List.TransformMany(P{1}, each P{0}, (i, _) => {_, i, null}),
Value.Type(Source)
),
{"Staff No.", "Date"},
{
{"Time In", each List.Min([Time])},
{"Time Out", each {null, List.Max([Time])}{Byte.From(List.NonNullCount([Time]) > 1)}}
}
),
{"Date", "Staff No."}
)
in
_
Power Query solution 2 for Pair Swipes, proposed by Zoran Milokanović:
let
Source = Table.Group(
Excel.CurrentWorkbook(){[Name = "Input"]}[Content],
{"Staff No.", "Date"},
{{"Time In", each List.Min([Time])}, {"Time Out", each List.Max([Time])}}
),
R = Table.ToRows(Source),
N = {null, null},
D = each List.Sort(List.Distinct(List.Zip(R){_})),
_ = Table.FromRows(
List.TransformMany(
D(1),
each D(0),
(i, _) => {_, i}
& List.FirstN(
List.Distinct(List.LastN(List.Select(R, (r) => r{0} = _ and r{1} = i){0}? ?? N, 2)) & N,
2
)
),
Value.Type(Source)
)
in
_
Power Query solution 3 for Pair Swipes, proposed by Kris Jaganah:
let
A = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content],
B = List.Distinct(A[#"Staff No."]),
C = List.Distinct(A[Date]),
D = Table.FromRows(List.TransformMany(B, each C, (x, y) => {x, y}), {"Staff No", "Date"}),
E = Table.AddColumn(
D,
"Ans",
each [
a = Table.SelectRows(A, (v) => v[#"Staff No."] = [Staff No] and v[Date] = [Date])[Time],
b = Time.From(List.Min(a)),
c = Time.From(List.Max(a)),
d = if b = c then null else c
]
),
F = Table.ExpandRecordColumn(E, "Ans", {"b", "d"}, {"Time In", "Time Out"}),
G = Table.Sort(F, {{"Date", 0}, {"Staff No", 0}})
in
G
Power Query solution 4 for Pair Swipes, proposed by Brian Julius:
let
Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content],
ReType = Table.TransformColumnTypes(
Source,
{{"Staff No.", type text}, {"Date", type date}, {"Time", type time}}
),
Group = Table.Group(
ReType,
{"Staff No.", "Date"},
{{"Time In", each List.Min([Time])}, {"TimeOut", each List.Max([Time])}}
),
CrossJ = Table.PrefixColumns(
Table.AddColumn(
Table.Distinct(Table.SelectColumns(Group, {"Date"})),
"Staff No.",
each Table.Distinct(Table.SelectColumns(Group, {"Staff No."}))
),
"x"
),
Expand = Table.ExpandTableColumn(CrossJ, "x.Staff No.", {"Staff No."}, {"x.Staff No."}),
Join = Table.Join(
Expand,
{"x.Staff No.", "x.Date"},
Group,
{"Staff No.", "Date"},
JoinKind.LeftOuter
),
Rem = Table.RemoveColumns(Join, {"Staff No.", "Date"}),
Reord = Table.ReorderColumns(Rem, {"x.Staff No.", "x.Date", "Time In", "TimeOut"}),
Ren = Table.RenameColumns(Reord, {{"x.Staff No.", "Staff No."}, {"x.Date", "Date"}}),
AddTOut = Table.AddColumn(Ren, "Time Out", each if [Time In] = [TimeOut] then null else [TimeOut]),
Rem2 = Table.RemoveColumns(AddTOut, {"TimeOut"}),
Sort = Table.Sort(Rem2, {{"Date", Order.Ascending}, {"Staff No.", Order.Ascending}})
in
Sort
Power Query solution 5 for Pair Swipes, proposed by Abdallah Ally:
let
Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content],
Transform = List.TransformMany(
List.Distinct(List.Sort(Source[Date])),
each List.Distinct(List.Sort(Source[#"Staff No."])),
(x, y) => {y, x}
),
FromRows = Table.FromRows(Transform, {"Staff No.", "Date"}),
AddCol = Table.AddColumn(
FromRows,
"Data",
each [
a = Table.SelectRows(Source, (x) => x[Date] = [Date] and x[#"Staff No."] = [#"Staff No."]),
b = {List.Min(a[Time], null), List.Max(a[Time], null)},
c = [#"Time In" = b{0}, #"Time Out" = if b{0} = b{1} then null else b{1}]
][c]
),
Expand = Table.ExpandRecordColumn(AddCol, "Data", {"Time In", "Time Out"}),
Result = Table.TransformColumnTypes(
Expand,
{{"Date", type date}, {"Time In", type time}, {"Time Out", type time}}
)
in
Result
Power Query solution 6 for Pair Swipes, proposed by Meganathan Elumalai:
let
Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content],
Lst = List.TransformMany(
List.Sort(List.Distinct(Source[#"Staff No."])),
(x) => List.Distinct(Source[Date]),
(x, y) => {x, y}
),
Tbl = Table.FromRows(
List.Sort(
List.Transform(
Lst,
(f) =>
[
L = Table.SelectRows(Source, (x) => x[#"Staff No."] = f{0} and x[Date] = f{1})[Time],
fin = f
& {
Time.From(List.Min(L)),
if List.Max(L) = List.Min(L) then null else Time.From(List.Max(L))
}
][fin]
),
{each _{1}, each _{0}}
),
{"Staff No", "Date", "Time In", "Time Out"}
),
Result = Table.TransformColumnTypes(
Tbl,
{{"Date", type date}, {"Time In", type time}, {"Time Out", type time}}
)
in
Result
Power Query solution 7 for Pair Swipes, proposed by Peter Krkos:
let
GroupedRows = Table.Group(
Source,
{"Staff No.", "Date"},
{{"Time In", each List.Min([Time]), type time}, {"Time Out", each List.Max([Time]), type time}}
),
Dates = List.Distinct(GroupedRows[Date]),
GroupedRows2 = Table.Group(
GroupedRows,
{"Staff No."},
{{"Date", each List.RemoveMatchingItems(Dates, [Date]){0}?, type date}}
),
FilteredRows = Table.SelectRows(GroupedRows2, each ([Date] <> null)),
Combined = Table.Combine({GroupedRows, FilteredRows}),
SortedRows = Table.Sort(Combined, {{"Date", Order.Ascending}, {"Staff No.", Order.Ascending}})
in
SortedRows
Power Query solution 8 for Pair Swipes, proposed by Krzysztof Kominiak:
let
Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content],
A = Table.TransformColumnTypes(
Source,
{{"Staff No.", type text}, {"Date", type date}, {"Time", type time}}
),
B = Table.Group(
A,
{"Staff No.", "Date"},
{
{
"NT",
each [
#"Time in" = List.Min([Time]),
#"Time out" = if List.Count([Time]) = 1 then null else List.Max([Time])
]
}
}
),
C = Table.Sort(
Table.ExpandListColumn(
Table.AddColumn(
Table.Sort(Table.Distinct(A[[#"Staff No."]]), "Staff No."),
"Date",
each List.Sort(List.Distinct(A[Date]))
),
"Date"
),
{{"Date", Order.Ascending}, {"Staff No.", Order.Ascending}}
),
D = Table.NestedJoin(
C,
{"Staff No.", "Date"},
B,
{"Staff No.", "Date"},
"Tab",
JoinKind.LeftOuter
),
E = Table.ExpandTableColumn(D, "Tab", {"NT"}),
Result = Table.ExpandRecordColumn(E, "NT", {"Time in", "Time out"}, {"Time in", "Time out"})
in
Result
Power Query solution 9 for Pair Swipes, proposed by Aleksandar Kovacevic:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
Grp =
Table.ExpandListColumn(
Table.Group(
Source,
{"Staff No."},
{
{"All", each _ },
{ "Date", each List.Distinct( Source[Date] ) }
}
),
"Date"
),
Trs =
Table.ExpandRecordColumn(
Table.AddColumn(
Grp,
"R",
each
[
L = Table.SelectRows( [All], (x) => _[Date] = x[Date] )[Time],
Time In = List.Min( L ),
Time Out = if List.Count( L ) > 1 then List.Max( L ) else null
]
),
"R", { "Time In", "Time Out" }
),
Res =
Table.TransformColumnTypes(
Table.Sort( Table.RemoveColumns( Trs, { "All" } ), { "Date", "Staff No." } ),
{{"Staff No.", type text}, {"Date", type date}, {"Time In", type time}, {"Time Out", type time}}
)
in
Power Query solution 10 for Pair Swipes, proposed by Nelson Mwangi:
let
Source = Excel.CurrentWorkbook(){[Name = "Table2"]}[Content],
UniqueStaffNo = Table.Distinct(Table.SelectColumns(Source, {"Staff No."})),
AddDateList = Table.AddColumn(UniqueStaffNo, "Date", each List.Distinct(Source[Date])),
ExpandDates = Table.ExpandListColumn(AddDateList, "Date"),
Group = Table.Group(
Source,
{"Staff No.", "Date"},
{
{"TimeIn", each List.Min([Time]), type number},
{"Time_Out", each List.Max([Time]), type number}
}
),
TimeOut = Table.AddColumn(
Group,
"TimeOut",
each if [TimeIn] = [Time_Out] then null else [Time_Out]
),
LeftJoin = Table.NestedJoin(
ExpandDates,
{"Staff No.", "Date"},
TimeOut,
{"Staff No.", "Date"},
"Merge"
),
Expand = Table.ExpandTableColumn(LeftJoin, "Merge", {"TimeIn", "TimeOut"}),
DataType = Table.TransformColumnTypes(Expand, {{"Date", type date}, {"Staff No.", type text}}),
Sort = Table.Sort(DataType, {{"Date", Order.Ascending}, {"Staff No.", Order.Ascending}})
in
Sort
Solving the challenge of Pair Swipes with Excel
Excel solution 1 for Pair Swipes, proposed by Kris Jaganah:
=LET(
a,
B4:B19,
b,
C4:C19,
c,
SORT(
TOCOL(
UNIQUE(
a)&" "&TOROW(
UNIQUE(
b)))),
d,
TEXTSPLIT(
c,
,
" "),
e,
--TEXTAFTER(
c,
" "),
f,
SORT(
B4:D19,
3),
g,
LAMBDA(
x,
XLOOKUP(
d&e,
BYROW(
DROP(
f,
,
-1),
CONCAT),
TAKE(
f,
,
-1),
"",
,
x)),
h,
g(
1),
i,
g(
-1),
VSTACK(
{"Staff No.",
"Date",
"Time In",
"Time Out"},
SORT(
HSTACK(
d,
e,
h,
IF(
h=i,
"",
i)),
{2,
1})))
Excel solution 2 for Pair Swipes, proposed by Hussein SATOUR:
=LET(
H,
HSTACK,
s,
B4:B19,
d,
C4:C19,
a,
TEXTSPLIT(
CONCAT(
UNIQUE(
s)&"/"&TOROW(
UNIQUE(
d))&";,"),
{";",
"/"},
","),
b,
DROP(
IFERROR(
--a,
a),
-1),
f,
LAMBDA(
x,
y,
VSTACK(
x,
INDEX(
b,
,
y))),
DROP(
GROUPBY(
H(
f(
d,
2),
f(
s,
1),
f(
d,
2)),
f(
D4:D19,
3),
H(
LAMBDA(
w,
IF(
MIN(
w)=0,
"",
MIN(
w))),
LAMBDA(
z,
IF(
MAX(
z)=MIN(
z),
"",
MAX(
z)))),
,
0),
,
1))
Excel solution 3 for Pair Swipes, proposed by Oscar Mendez Roca Farell:
=LET(
b,
B4:B19,
c,
C4:C19,
s,
SORT(
UNIQUE(
b)),
d,
TOROW(
UNIQUE(
c)),
F,
LAMBDA(
F,
TOCOL(
F(
D4:D19,
b,
s,
c,
d),
,
1)),
i,
F(
MINIFS),
a,
F(
MAXIFS),
t,
TOCOL(
s&-d,
,
1),
HSTACK(
MID(
t,
1,
7),
--MID(
t,
9,
5),
IF(
i,
i,
""),
IF(
a-i,
a,
"")))
Excel solution 4 for Pair Swipes, proposed by Duy Tùng:
=LET(
b,
B4:B19,
c,
C4:C19,
a,
DROP(
REDUCE(
0,
UNIQUE(
c),
LAMBDA(
x,
y,
VSTACK(
x,
IF(
{1,
0},
SORT(
UNIQUE(
b)),
y)))),
1),
f,
LAMBDA(
h,
IFERROR(
MAP(
BYROW(
a,
CONCAT),
LAMBDA(
x,
h(
FILTER(
D4:D19,
x=b&c)))),
"")),
HSTACK(
a,
f(
MIN),
IF(
f(
MAX)=f(
MIN),
"",
f(
MAX))))
Excel solution 5 for Pair Swipes, proposed by Pieter de B.:
=LET(
b,
B4:B19,
c,
C4:C19,
u,
UNIQUE,
x,
TOCOL(
u(
b)&-TOROW(
u(
C4:C19))),
L,
LAMBDA(
y,
XLOOKUP(
x,
b&-c,
D4:D19,
"",
,
y)),
s,
TEXTSPLIT(
TEXTAFTER(
"-"&x,
"-",
{1,
2}),
"-"),
a,
L(
-1),
z,
L(
1),
SORT(
GROUPBY(
IFERROR(
--s,
s),
HSTACK(
a,
IFERROR(
IF(
z-a,
z,
""),
"")),
SINGLE,
,
0),
2))
Excel solution 6 for Pair Swipes, proposed by Hamidi Hamid:
=LET(
p,
LAMBDA(
z,
m,
TAKE(
z,
,
m)),
x,
SORT(
UNIQUE(
B4:B19)),
y,
TOROW(
UNIQUE(
C4:C19)),
f,
LAMBDA(
s,
q,
TOCOL(
IFNA(
s,
q),
,
1)),
au,
HSTACK(
f(
x,
y),
f(
y,
x)),
tu,
XLOOKUP(
p(
au,
1)&p(
au,
-1),
B4:B19&C4:C19,
D4:D19,
"",
,
{-1,
1}),
td,
IFERROR(
IF(
p(
tu,
-1)*1=p(
tu,
1)*1,
"",
p(
tu,
-1)),
""),
HSTACK(
au,
p(
tu,
1),
td))
Excel solution 7 for Pair Swipes, proposed by Asheesh Pahwa:
=LET(
a,
ARRAYTOTEXT(
MAP(
UNIQUE(
C4:C19),
LAMBDA(
x,
ARRAYTOTEXT(
SORT(
UNIQUE(
F4:F17))&"-"&x)))),
t,
TEXTSPLIT(
a,
"-",
", "),
p,
B4:B19&C4:C19,
_t,
TAKE(
t,
,
1)&TAKE(
t,
,
-1),
u,
_t,
HSTACK(
t,
IFNA(
DROP(
REDUCE(
"",
_t,
LAMBDA(
x,
y,
VSTACK(
x,
LET(
f,
FILTER(
D4:D19,
p=y,
""),
s,
SORT(
f),
IF(
COUNT(
s)>1,
TAKE(
s,
{1,
-1}),
s))))),
1),
"")))
Excel solution 8 for Pair Swipes, proposed by Meganathan Elumalai:
=LET(x,
DROP(
REDUCE(
"",
UNIQUE(
C4:C19),
LAMBDA(
a,
v,
VSTACK(
a,
IF(
{1,
0},
SORT(
UNIQUE(
B4:B19)),
v)))),
1),
l,
LAMBDA(fx,
MAP(TAKE(
x,
,
1),
DROP(
x,
,
1),
LAMBDA(p,
q,
fx(FILTER(D4:D19,
(B4:B19=p)*(C4:C19=q),
0))))),
HSTACK(
x,
l(
MIN),
IF(
l(
MAX)=l(
MIN),
"",
l(
MAX))))
Solving the challenge of Pair Swipes with Python
Python solution 1 for Pair Swipes, proposed by Konrad Gryczan, PhD:
import pandas as pd
from itertools import product
from datetime import time
path = "files/Challenge1325.xlsx"
input = pd.read_excel(path, sheet_name=0, usecols="B:D", skiprows=2, nrows=17)
test = pd.read_excel(path, sheet_name=0, usecols="F:I", skiprows=2, nrows=14).rename(columns=lambda x: x.replace('.1', ''))
df = pd.DataFrame(product(input['Staff No.'].unique(), input['Date'].unique()), columns=['Staff No.', 'Date'])
df = df.merge(input, on=['Staff No.', 'Date'], how='left')
df = df.sort_values(by=['Staff No.', 'Date', "Time"]).reset_index(drop=True)
df = df.groupby(['Staff No.', 'Date']).apply(lambda group: group.iloc[[0, -1]]).reset_index(drop=True)
df = df.drop_duplicates().reset_index(drop=True)
df['Count'] = df.groupby(['Staff No.', 'Date']).cumcount() + 1
df_pivot = df.pivot(index=['Staff No.', 'Date'], columns='Count', values='Time').reset_index()
df_pivot = df_pivot.sort_values(by=['Date', 'Staff No.']).reset_index(drop=True)
df_pivot.columns = ['Staff No.', 'Date', 'Time In', 'Time Out']
print(df_pivot)
Solving the challenge of Pair Swipes with Python in Excel
Python in Excel solution 1 for Pair Swipes, proposed by Alejandro Campos:
#PY
df = xl("B3:D19", True)
df["DateTime"] = pd.to_datetime(df["Date"].astype(str) + " " + df["Time"].astype(str))
grouped = df.groupby(["Staff No.", "Date"])["DateTime"].agg(["min", "max"]).reset_index()
grouped.columns = ["Staff No.", "Date", "Time_IN", "Time_OUT"]
grouped[["Time_IN", "Time_OUT"]] = grouped[["Time_IN", "Time_OUT"]].apply(lambda x: x.dt.strftime('%H:%M:%S'))
grouped.loc[grouped["Time_IN"] == grouped["Time_OUT"], "Time_OUT"] = ""
final_df = pd.merge(pd.MultiIndex.from_product([df["Staff No."].unique(), df["Date"].unique()],
names=["Staff No.", "Date"]).to_frame(index=False), grouped, on=["Staff No.", "Date"], how="left").fillna("")
final_df.sort_values(["Date", "Staff No."]).reset_index(drop=True)
Solving the challenge of Pair Swipes with R
R solution 1 for Pair Swipes, proposed by Konrad Gryczan, PhD:
library(tidyverse)
library(readxl)
path = "files/Challenge1325.xlsx"
input = read_excel(path, range = "B3:D19")
test = read_excel(path, range = "F3:I17") %>%
mutate(across(starts_with("Time"), ~hms::as_hms(.)))
expanded = expand.grid(unique(input$`Staff No.`), unique(input$Date)) %>%
left_join(input, by = c("Var1" = "Staff No.", "Var2" = "Date")) %>%
mutate(Time = hms::as_hms(Time)) %>%
arrange(Var1, Var2, Time) %>%
mutate(n = n(),
rn = row_number(),
.by = c("Var1", "Var2")) %>%
filter(rn == 1|rn == n) %>%
mutate(rn = row_number(), .by = c(Var1, Var2)) %>%
select(-n) %>%
pivot_wider(names_from = rn, values_from = Time) %>%
arrange(Var2, Var1)
colnames(expanded) = colnames(test)
print(expanded)
