Top rank per group: Difference between revisions

m
m (→‎{{header|Python}}: final for with join)
m (→‎{{header|Wren}}: Minor tidy)
 
(187 intermediate revisions by 70 users not shown)
Line 1:
{{task}}
{{task}}Find the top ''N'' salaries in each department, where ''N'' is provided as a parameter.
 
;Task:
Find the top   ''N''   salaries in each department,   where   ''N''   is provided as a parameter.
 
Use this data as a formatted internal data structure (adapt it to your language-native idioms, rather than parse at runtime), or identify your external data source:
<pre>
<pre>Employee Name,Employee ID,Salary,Department
Employee Name,Employee ID,Salary,Department
Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
Line 15 ⟶ 19:
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190</pre>
</pre>
<br>
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">V data = [(‘Tyler Bennett’, ‘E10297’, 32000, ‘D101’),
(‘John Rappl’, ‘E21437’, 47000, ‘D050’),
(‘George Woltman’, ‘E00127’, 53500, ‘D101’),
(‘Adam Smith’, ‘E63535’, 18000, ‘D202’),
(‘Claire Buckman’, ‘E39876’, 27800, ‘D202’),
(‘David McClellan’, ‘E04242’, 41500, ‘D101’),
(‘Rich Holcomb’, ‘E01234’, 49500, ‘D202’),
(‘Nathan Adams’, ‘E41298’, 21900, ‘D050’),
(‘Richard Potter’, ‘E43128’, 15900, ‘D101’),
(‘David Motsinger’, ‘E27002’, 19250, ‘D202’),
(‘Tim Sampair’, ‘E03033’, 27000, ‘D101’),
(‘Kim Arlich’, ‘E10001’, 57000, ‘D190’),
(‘Timothy Grove’, ‘E16398’, 29900, ‘D190’)]
 
DefaultDict[String, [(String, String, Int, String)]] departments
L(rec) data
departments[rec[3]].append(rec)
 
V n = 3
L(department, recs) sorted(departments.items())
print(‘Department #.’.format(department))
print(‘ #<15 #<15 #<15 #<15 ’.format(‘Employee Name’, ‘Employee ID’, ‘Salary’, ‘Department’))
L(rec) sorted(recs, key' rec -> rec[2], reverse' 1B)[0 .< n]
print(‘ #<15 #<15 #<15 #<15 ’.format(rec[0], rec[1], rec[2], rec[3]))
print()</syntaxhighlight>
 
{{out}}
<pre style="height:30ex;overflow:scroll>
Department D050
Employee Name Employee ID Salary Department
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
 
Department D101
Employee Name Employee ID Salary Department
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Tyler Bennett E10297 32000 D101
 
Department D190
Employee Name Employee ID Salary Department
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
 
Department D202
Employee Name Employee ID Salary Department
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
David Motsinger E27002 19250 D202
</pre>
 
=={{header|Action!}}==
<syntaxhighlight lang="action!">DEFINE PTR="CARD"
DEFINE ENTRY_SIZE="8"
 
TYPE Employee=[
PTR name,id,dep ;CHAR ARRAY
CARD salary]
 
BYTE ARRAY data(200)
BYTE count=[0]
 
PTR FUNC GetItemAddr(INT index)
PTR addr
 
addr=data+index*ENTRY_SIZE
RETURN (addr)
 
PROC Append(CHAR ARRAY n,i CARD s CHAR ARRAY d)
Employee POINTER dst
 
dst=GetItemAddr(count)
dst.name=n
dst.id=i
dst.dep=d
dst.salary=s
count==+1
RETURN
 
PROC InitData()
Append("Tyler Bennett","E10297",32000,"D101")
Append("John Rappl","E21437",47000,"D050")
Append("George Woltman","E00127",53500,"D101")
Append("Adam Smith","E63535",18000,"D202")
Append("Claire Buckman","E39876",27800,"D202")
Append("David McClellan","E04242",41500,"D101")
Append("Rich Holcomb","E01234",49500,"D202")
Append("Nathan Adams","E41298",21900,"D050")
Append("Richard Potter","E43128",15900,"D101")
Append("David Motsinger","E27002",19250,"D202")
Append("Tim Sampair","E03033",27000,"D101")
Append("Kim Arlich","E10001",57000,"D190")
Append("Timothy Grove","E16398",29900,"D190")
RETURN
 
PROC Swap(Employee POINTER e1,e2)
PTR tmp
 
tmp=e1.name e1.name=e2.name e2.name=tmp
tmp=e1.id e1.id=e2.id e2.id=tmp
tmp=e1.dep e1.dep=e2.dep e2.dep=tmp
tmp=e1.salary e1.salary=e2.salary e2.salary=tmp
RETURN
 
PROC Sort()
INT i,j,minpos,comp
Employee POINTER e1,e2
 
FOR i=0 TO count-2
DO
minpos=i
FOR j=i+1 TO count-1
DO
e1=GetItemAddr(minpos)
e2=GetItemAddr(j)
comp=SCompare(e1.dep,e2.dep)
IF comp>0 OR comp=0 AND e1.salary<e2.salary THEN
minpos=j
FI
OD
IF minpos#i THEN
e1=GetItemAddr(minpos)
e2=GetItemAddr(i)
Swap(e1,e2)
FI
OD
RETURN
 
PROC TopRank(BYTE n)
BYTE i,c
CHAR ARRAY d
Employee POINTER e
 
i=0
WHILE i<count
DO
e=GetItemAddr(i)
IF i=0 OR SCompare(e.dep,d)#0 THEN
d=e.dep c=0
IF i>0 THEN PutE() FI
PrintF("Department %S:%E",d)
c==+1
PrintF(" %U %S %S%E",e.salary,e.id,e.name)
ELSEIF c<n THEN
c==+1
PrintF(" %U %S %S%E",e.salary,e.id,e.name)
FI
i==+1
OD
RETURN
 
PROC Main()
InitData()
Sort()
TopRank(3)
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Top_rank_per_group.png Screenshot from Atari 8-bit computer]
<pre>
Department D050:
47000 E21437 John Rappl
21900 E41298 Nathan Adams
 
Department D101:
53500 E00127 George Woltman
41500 E04242 David McClellan
32000 E10297 Tyler Bennett
 
Department D190:
57000 E10001 Kim Arlich
29900 E16398 Timothy Grove
 
Department D202:
49500 E01234 Rich Holcomb
27800 E39876 Claire Buckman
19250 E27002 David Motsinger
</pre>
 
=={{header|Ada}}==
top.adb:
<langsyntaxhighlight Adalang="ada">with Ada.Containers.Vectors;
with Ada.Text_IO;
 
Line 89 ⟶ 277:
end;
end loop;
end Top;</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department D050
 
Output:
<pre>Department D050
John Rappl | E21437 | 47000
Nathan Adams | E41298 | 21900
Line 106 ⟶ 292:
Claire Buckman | E39876 | 27800
David Motsinger | E27002 | 19250</pre>
 
=={{header|Aime}}==
<syntaxhighlight lang="aime">Add_Employee(record employees, text name, id, integer salary, text department)
{
employees[name] = list(name, id, salary, department);
}
 
collect(record top, employees)
{
for (, list l in employees) {
top.v_index(l[3]).v_list(l[2]).link(-1, l);
}
for (text department, index x in top) {
list t;
 
x.ucall(l_ucall, 0, l_append, 1, t);
if (N < ~t.reverse) {
t.erase(N, -1);
}
top[department] = t;
}
}
 
print_department(text department, list employees)
{
o_("Department ", department, "\n");
 
for (, list l in employees) {
o_form(" ~ | ~ | ~\n", l[0], l[1], l[2]);
}
}
 
main(void)
{
record employees, top;
 
Add_Employee(employees, "Tyler Bennett ", "E10297", 32000, "D101");
Add_Employee(employees, "John Rappl ", "E21437", 47000, "D050");
Add_Employee(employees, "George Woltman ", "E00127", 53500, "D101");
Add_Employee(employees, "Adam Smith ", "E63535", 18000, "D202");
Add_Employee(employees, "Claire Buckman ", "E39876", 27800, "D202");
Add_Employee(employees, "David McClellan", "E04242", 41500, "D101");
Add_Employee(employees, "Rich Holcomb ", "E01234", 49500, "D202");
Add_Employee(employees, "Nathan Adams ", "E41298", 21900, "D050");
Add_Employee(employees, "Richard Potter ", "E43128", 15900, "D101");
Add_Employee(employees, "David Motsinger", "E27002", 19250, "D202");
Add_Employee(employees, "Tim Sampair ", "E03033", 27000, "D101");
Add_Employee(employees, "Kim Arlich ", "E10001", 57000, "D190");
Add_Employee(employees, "Timothy Grove ", "E16398", 29900, "D190");
 
collect(top, employees);
 
top.wcall(print_department, 0, 1);
 
0;
}</syntaxhighlight>Run as:
<pre>aime rcs/top_rank_per_group c N 5</pre>{{out}}
<pre style="height:30ex;overflow:scroll">Department D050
John Rappl | E21437 | 47000
Nathan Adams | E41298 | 21900
Department D101
George Woltman | E00127 | 53500
David McClellan | E04242 | 41500
Tyler Bennett | E10297 | 32000
Tim Sampair | E03033 | 27000
Richard Potter | E43128 | 15900
Department D190
Kim Arlich | E10001 | 57000
Timothy Grove | E16398 | 29900
Department D202
Rich Holcomb | E01234 | 49500
Claire Buckman | E39876 | 27800
David Motsinger | E27002 | 19250
Adam Smith | E63535 | 18000</pre>
 
=={{header|ALGOL 68}}==
Using code from the [[Sort using a custom comparator#ALGOL 68|ALGOL 68 sample in the Sort using a custom comparator task]].
<syntaxhighlight lang="algol68">
BEGIN # show the top rank salaries per department #
# NODE to hold the employee data - will be the MODE that is sorted #
MODE SITEM = STRUCT( STRING employee name
, STRING employee id
, INT salary
, STRING department
);
# ---- begin code from the quicksort using a custom comparator task ----- #
#--- Swap function ---#
PROC swap = (REF[]SITEM array, INT first, INT second) VOID:
(
SITEM temp := array[first];
array[first] := array[second];
array[second]:= temp
);
#--- Quick sort partition arg function with custom comparision function ---#
PROC quick = (REF[]SITEM array, INT first, INT last, PROC(SITEM,SITEM)INT compare) VOID:
(
INT smaller := first + 1,
larger := last;
SITEM pivot := array[first];
WHILE smaller <= larger DO
WHILE compare(array[smaller], pivot) < 0 AND smaller < last DO
smaller +:= 1
OD;
WHILE compare( array[larger], pivot) > 0 AND larger > first DO
larger -:= 1
OD;
IF smaller < larger THEN
swap(array, smaller, larger);
smaller +:= 1;
larger -:= 1
ELSE
smaller +:= 1
FI
OD;
swap(array, first, larger);
IF first < larger-1 THEN
quick(array, first, larger-1, compare)
FI;
IF last > larger +1 THEN
quick(array, larger+1, last, compare)
FI
);
#--- Quick sort array function with custom comparison function ---#
PROC quicksort = (REF[]SITEM array, PROC(SITEM,SITEM)INT compare) VOID:
(
IF UPB array > LWB array THEN
quick(array, LWB array, UPB array, compare)
FI
);
# ==== end code from the quicksort using a custom comparator task ===== #
 
# sort comparison routine - sort ascending department, descending salary #
PROC sort by department then salary = ( SITEM a, SITEM b )INT:
IF department OF a < department OF b THEN -1
ELIF department OF a > department OF b THEN 1
ELIF salary OF a > salary OF b THEN -1
ELIF salary OF a < salary OF b THEN 1
ELSE 0
FI # sort by department then salary # ;
 
# returns s blank padded on the right to at least w characters #
PROC pad right = ( STRING s, INT w )STRING:
IF INT len = ( UPB s - LWB s ) + 1; len >= w THEN s ELSE s + ( ( w - len ) * " " ) FI;
# shows the top n ranked salaries in each department #
PROC show top rank = ( []SITEM data, INT n )VOID:
BEGIN
# copy the data and sort it #
[ LWB data : UPB data ]SITEM sorted data := data;
quicksort( sorted data, sort by department then salary );
# show the top salaries per department #
INT d pos := LWB sorted data;
WHILE d pos <= UPB sorted data DO
STRING curr department := department OF sorted data[ d pos ];
print( ( "Department: ", curr department, newline ) );
INT e count := 0;
WHILE IF e count < n THEN
print( ( " "
, employee id OF sorted data[ d pos ]
, " "
, pad right( employee name
OF sorted data[ d pos ], 24 )
, " "
, whole( salary OF sorted data[ d pos ], -6 )
, newline
)
)
FI;
e count +:= 1;
d pos +:= 1;
IF d pos > UPB sorted data
THEN FALSE
ELSE curr department = department OF sorted data[ d pos ]
FI
DO SKIP OD;
print( ( newline ) )
OD
END # show top rank # ;
 
# employee data #
[]SITEM employees = ( ( "Tyler Bennett", "E10297", 32000, "D101" )
, ( "John Rappl", "E21437", 47000, "D050" )
, ( "George Woltman", "E00127", 53500, "D101" )
, ( "Adam Smith", "E63535", 18000, "D202" )
, ( "Claire Buckman", "E39876", 27800, "D202" )
, ( "David McClellan", "E04242", 41500, "D101" )
, ( "Rich Holcomb", "E01234", 49500, "D202" )
, ( "Nathan Adams", "E41298", 21900, "D050" )
, ( "Richard Potter", "E43128", 15900, "D101" )
, ( "David Motsinger", "E27002", 19250, "D202" )
, ( "Tim Sampair", "E03033", 27000, "D101" )
, ( "Kim Arlich", "E10001", 57000, "D190" )
, ( "Timothy Grove", "E16398", 29900, "D190" )
);
# show the top two salaries by department #
show top rank( employees, 2 )
END
</syntaxhighlight>
{{out}}
<pre>
Department: D050
E21437 John Rappl 47000
E41298 Nathan Adams 21900
 
Department: D101
E00127 George Woltman 53500
E04242 David McClellan 41500
 
Department: D190
E10001 Kim Arlich 57000
E16398 Timothy Grove 29900
 
Department: D202
E01234 Rich Holcomb 49500
E39876 Claire Buckman 27800
</pre>
 
=={{header|AppleScript}}==
It looks as if this task's requirements have changed — possibly more than once — since it was first set and named. As at the end of May 2021, it specifies the return of the top N ''salaries'' in each department and makes no mention at all of any of the other employee details. This makes it easier to decide how to handle the other thing it doesn't mention: what to return when two or more people in the same department are on the same pay. The interpretation here is "top N ''discrete'' salary values" (or of course fewer if a department doesn't have that many).
 
<syntaxhighlight lang="applescript">use AppleScript version "2.3.1" -- Mac OS X 10.9 (Mavericks) or later.
use sorter : script ¬
"Custom Iterative Ternary Merge Sort" -- <www.macscripter.net/t/timsort-and-nigsort/71383/3>
 
on topNSalariesPerDepartment(employeeRecords, n)
set output to {}
set employeeCount to (count employeeRecords)
if ((employeeCount > 0) and (n > 0)) then
-- Sort a copy of the employee record list by department
-- with descending subsorts on salary.
copy employeeRecords to employeeRecords
script comparer
on isGreater(a, b)
return ((a's department > b's department) or ¬
((a's department = b's department) and (a's salary < b's salary)))
end isGreater
end script
considering numeric strings
tell sorter to sort(employeeRecords, 1, employeeCount, {comparer:comparer})
end considering
-- Initialise the output with data from the first record in the sorted list,
-- then work through the rest of the list.
set {department:previousDepartment, salary:previousSalary} to beginning of employeeRecords
set {mv, topSalaries} to {"-", {previousSalary}}
set end of output to {department:previousDepartment, salaries:topSalaries}
repeat with i from 2 to employeeCount
set {department:thisDepartment, salary:thisSalary} to item i of employeeRecords
if (thisDepartment = previousDepartment) then
if ((thisSalary < previousSalary) and ((count topSalaries) < n)) then
set end of topSalaries to thisSalary
set previousSalary to thisSalary
end if
else
-- First record of the next department.
-- Pad out the previous department's salary list if it has fewer than n entries.
repeat (n - (count topSalaries)) times
set end of topSalaries to mv
end repeat
-- Start a result record for the new department and add it to the output.
set topSalaries to {thisSalary}
set end of output to {department:thisDepartment, salaries:topSalaries}
set previousDepartment to thisDepartment
set previousSalary to thisSalary
end if
end repeat
-- Pad the last department's salary list if necessary.
repeat (n - (count topSalaries)) times
set end of topSalaries to mv
end repeat
end if
return output
end topNSalariesPerDepartment
 
on join(lst, delim)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to delim
set txt to lst as text
set AppleScript's text item delimiters to astid
return txt
end join
 
on task()
set employeeRecords to {¬
{|name|:"Tyler Bennett", |ID|:"E10297", salary:32000, department:"D101"}, ¬
{|name|:"John Rappl", |ID|:"E21437", salary:47000, department:"D050"}, ¬
{|name|:"George Woltman", |ID|:"E00127", salary:53500, department:"D101"}, ¬
{|name|:"Adam Smith", |ID|:"E63535", salary:18000, department:"D202"}, ¬
{|name|:"Claire Buckman", |ID|:"E39876", salary:27800, department:"D202"}, ¬
{|name|:"David McClellan", |ID|:"E04242", salary:41500, department:"D101"}, ¬
{|name|:"Rich Holcomb", |ID|:"E01234", salary:49500, department:"D202"}, ¬
{|name|:"Nathan Adams", |ID|:"E41298", salary:21900, department:"D050"}, ¬
{|name|:"Richard Potter", |ID|:"E43128", salary:15900, department:"D101"}, ¬
{|name|:"David Motsinger", |ID|:"E27002", salary:19250, department:"D202"}, ¬
{|name|:"Tim Sampair", |ID|:"E03033", salary:27000, department:"D101"}, ¬
{|name|:"Kim Arlich", |ID|:"E10001", salary:57000, department:"D190"}, ¬
{|name|:"Timothy Grove", |ID|:"E16398", salary:29900, department:"D190"}, ¬
{|name|:"Simila Pey", |ID|:"E16399", salary:29900, department:"D190"} ¬
}
set n to 4
set topSalaryRecords to topNSalariesPerDepartment(employeeRecords, n)
-- Derive a text report from the result.
set report to {"Top " & n & " salaries per department:"}
repeat with thisRecord in topSalaryRecords
set end of report to thisRecord's department & ": " & join(thisRecord's salaries, " ")
end repeat
return join(report, linefeed)
end task
 
task()</syntaxhighlight>
 
{{output}}
<syntaxhighlight lang="applescript">"Top 4 salaries per department:
D050: 47000 21900 - -
D101: 53500 41500 32000 27000
D190: 57000 29900 - -
D202: 49500 27800 19250 18000"</syntaxhighlight>
 
=={{header|Arturo}}==
 
<syntaxhighlight lang="rebol">printTopEmployees: function [count, entries][
data: read.csv.withHeaders entries
departments: sort unique map data 'row -> row\Department
loop departments 'd [
print "----------------------"
print ["department:" d]
print "----------------------"
loop first.n: count
sort.descending.by:"Salary"
select data 'row [row\Department = d] 'employee ->
print [get employee "Employee Name" "->" employee\Salary]
print ""
]
]
 
printTopEmployees 2 {
Employee Name,Employee ID,Salary,Department
Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190
}</syntaxhighlight>
 
{{out}}
 
<pre>----------------------
department: D050
----------------------
John Rappl -> 47000
Nathan Adams -> 21900
 
----------------------
department: D101
----------------------
George Woltman -> 53500
David McClellan -> 41500
 
----------------------
department: D190
----------------------
Kim Arlich -> 57000
Timothy Grove -> 29900
 
----------------------
department: D202
----------------------
Rich Holcomb -> 49500
Claire Buckman -> 27800</pre>
 
=={{header|AutoHotkey}}==
<langsyntaxhighlight lang="autohotkey">Departments = D050, D101, D190, D202
StringSplit, Department_, Departments, `,, %A_Space%
 
Line 125 ⟶ 689:
Add_Employee("Kim Arlich ", "E10001", 57000, "D190")
Add_Employee("Timothy Grove ", "E16398", 29900, "D190")
 
 
; display top 3 ranks for each department
Line 131 ⟶ 694:
MsgBox,, % "Department: " Department_%A_Index%
, % TopRank(3, Department_%A_Index%)
 
 
;---------------------------------------------------------------------------
Line 155 ⟶ 717:
Return, Msg
}
 
 
;---------------------------------------------------------------------------
Line 166 ⟶ 727:
Employee_%m%_Salary := Salary
Employee_%m%_Dept := Department
}</syntaxhighlight>{{out}}
}</lang>
<pre style="height:30ex;overflow:scroll">Department: D050
Message boxes show:
<pre>Department: D050
---------------------------
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050</pre>
 
<pre>Department: D101
Department: D101
---------------------------
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Tyler Bennett E10297 32000 D101</pre>
 
<pre>Department: D190
Department: D190
---------------------------
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190</pre>
 
<pre>Department: D202
Department: D202
---------------------------
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
David Motsinger E27002 19250 D202</pre>
 
=={{header|AWK}}==
<syntaxhighlight lang="awk">
# syntax: GAWK -f TOP_RANK_PER_GROUP.AWK [n]
#
# sorting:
# PROCINFO["sorted_in"] is used by GAWK
# SORTTYPE is used by Thompson Automation's TAWK
#
BEGIN {
arrA[++n] = "Employee Name,Employee ID,Salary,Department" # raw data
arrA[++n] = "Tyler Bennett,E10297,32000,D101"
arrA[++n] = "John Rappl,E21437,47000,D050"
arrA[++n] = "George Woltman,E00127,53500,D101"
arrA[++n] = "Adam Smith,E63535,18000,D202"
arrA[++n] = "Claire Buckman,E39876,27800,D202"
arrA[++n] = "David McClellan,E04242,41500,D101"
arrA[++n] = "Rich Holcomb,E01234,49500,D202"
arrA[++n] = "Nathan Adams,E41298,21900,D050"
arrA[++n] = "Richard Potter,E43128,15900,D101"
arrA[++n] = "David Motsinger,E27002,19250,D202"
arrA[++n] = "Tim Sampair,E03033,27000,D101"
arrA[++n] = "Kim Arlich,E10001,57000,D190"
arrA[++n] = "Timothy Grove,E16398,29900,D190"
for (i=2; i<=n; i++) { # build internal structure
split(arrA[i],arrB,",")
arrC[arrB[4]][arrB[3]][arrB[2] " " arrB[1]] # I.E. arrC[dept][salary][id " " name]
}
show = (ARGV[1] == "") ? 1 : ARGV[1] # employees to show per department
printf("DEPT SALARY EMPID NAME\n\n") # produce report
PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
for (i in arrC) {
PROCINFO["sorted_in"] = "@ind_str_desc" ; SORTTYPE = 9
shown = 0
for (j in arrC[i]) {
PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
for (k in arrC[i][j]) {
if (shown++ < show) {
printf("%-4s %6s %s\n",i,j,k)
printed++
}
}
}
if (printed > 0) { print("") }
printed = 0
}
exit(0)
}
</syntaxhighlight>
<p>Sample command and output:</p>
<pre>
GAWK -f TOP_RANK_PER_GROUP.AWK 3
 
DEPT SALARY EMPID NAME
 
D050 47000 E21437 John Rappl
D050 21900 E41298 Nathan Adams
 
D101 53500 E00127 George Woltman
D101 41500 E04242 David McClellan
D101 32000 E10297 Tyler Bennett
 
D190 57000 E10001 Kim Arlich
D190 29900 E16398 Timothy Grove
 
D202 49500 E01234 Rich Holcomb
D202 27800 E39876 Claire Buckman
D202 19250 E27002 David Motsinger
</pre>
 
=={{header|Bracmat}}==
Bracmat has no dedicated sorting functions. However, when evaluating algebraic expressions, Bracmat sorts factors in products and terms in sums. Moreover, Bracmat simplifies products by, amongst other transformations, collecting exponents of the same base into a single exponent, which is the sum of the collected exponents: <code>a^b*a^c</code> &rarr; <code>a^(b+c)</code>. This built-in behaviour is made use of in this solution.
<syntaxhighlight lang="bracmat"> (Tyler Bennett,E10297,32000,D101)
(John Rappl,E21437,47000,D050)
(George Woltman,E00127,53500,D101)
(Adam Smith,E63535,18000,D202)
(Claire Buckman,E39876,27800,D202)
(David McClellan,E04242,41500,D101)
(Rich Holcomb,E01234,49500,D202)
(Nathan Adams,E41298,21900,D050)
(Richard Potter,E43128,15900,D101)
(David Motsinger,E27002,19250,D202)
(Tim Sampair,E03033,27000,D101)
(Kim Arlich,E10001,57000,D190)
(Timothy Grove,E16398,29900,D190)
: ?employees
& ( toprank
= employees N n P "Employee Name" "Employee ID" SalaryDepartment
. !arg:(?employees.?N)
& 1:?P
& whl
' ( !employees
: (?"Employee Name",?"Employee ID",?Salary,?Department)
?employees
& !Department^(!Salary.!"Employee Name".!"Employee ID")*!P:?P
)
& out$(Top !N "salaries per department.")
& whl
' ( !P:%?Department^?employees*?P
& out$(str$("Department " !Department ":"))
& !N:?n
& whl
' ( !n+-1:~<0:?n
& !employees
: ?employees+(?Salary.?"Employee Name".?"Employee ID")
& out
$ (str$(" " !"Employee Name" " (" !"Employee ID" "):" !Salary))
)
)
)
& toprank$(!employees.3)
& ;</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Top 3 salaries per department.
Department D050:
JohnRappl (E21437):47000
NathanAdams (E41298):21900
Department D101:
GeorgeWoltman (E00127):53500
DavidMcClellan (E04242):41500
TylerBennett (E10297):32000
Department D190:
KimArlich (E10001):57000
TimothyGrove (E16398):29900
Department D202:
RichHolcomb (E01234):49500
ClaireBuckman (E39876):27800
DavidMotsinger (E27002):19250</pre>
 
=={{header|C}}==
<langsyntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct {
const char *name, *id, *dept;
int sal;
} person;
 
person ppl[] = {
{"Tyler Bennett", "E10297", "D101", 32000},
{"John Rappl", "E21437", "D050", 47000},
{"George Woltman", "E00127", "D101", 53500},
{"Adam Smith", "E63535", "D202", 18000},
{"Claire Buckman", "E39876", "D202", 27800},
{"David McClellan", "E04242", "D101", 41500},
{"Rich Holcomb", "E01234", "D202", 49500},
{"Nathan Adams", "E41298", "D050", 21900},
{"Richard Potter", "E43128", "D101", 15900},
{"David Motsinger", "E27002", "D202", 19250},
{"Tim Sampair", "E03033", "D101", 27000},
{"Kim Arlich", "E10001", "D190", 57000},
{"Timothy Grove", "E16398", "D190", 29900},
};
 
int pcmp(const void *a, const void *b)
{
const person *aa = a, *bb = b;
int x = strcmp(aa->dept, bb->dept);
if (x) return x;
return aa->sal > bb->sal ? -1 : aa->sal < bb->sal;
}
 
Line 224 ⟶ 914:
void top(int n)
{
int i, rank;
qsort(ppl, N, sizeof(person), pcmp);
 
for (i = rank = 0; i < N; i++) {
if (i && strcmp(ppl[i].dept, ppl[i - 1].dept)) {
rank = 0;
printf("\n");
}
}
 
if (rank++ < n)
printf("%s %d: %s\n", ppl[i].dept, ppl[i].sal, ppl[i].name);
}
}
}
 
int main()
{
top(2);
return 0;
}</syntaxhighlight>{{out}}
}</lang>output
<pre>D050 47000: John Rappl
D050 21900: Nathan Adams
Line 254 ⟶ 944:
D202 49500: Rich Holcomb
D202 27800: Claire Buckman</pre>
 
=={{header|C sharp|C#}}==
 
<syntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
using System.Linq;
 
public class Program
{
class Employee
{
public Employee(string name, string id, int salary, string department)
{
Name = name;
Id = id;
Salary = salary;
Department = department;
}
 
public string Name { get; private set; }
public string Id { get; private set; }
public int Salary { get; private set; }
public string Department { get; private set; }
 
public override string ToString()
{
return String.Format("{0, -25}\t{1}\t{2}", Name, Id, Salary);
}
}
 
private static void Main(string[] args)
{
var employees = new List<Employee>
{
new Employee("Tyler Bennett", "E10297", 32000, "D101"),
new Employee("John Rappl", "E21437", 47000, "D050"),
new Employee("George Woltman", "E21437", 53500, "D101"),
new Employee("Adam Smith", "E21437", 18000, "D202"),
new Employee("Claire Buckman", "E39876", 27800, "D202"),
new Employee("David McClellan", "E04242", 41500, "D101"),
new Employee("Rich Holcomb", "E01234", 49500, "D202"),
new Employee("Nathan Adams", "E41298", 21900, "D050"),
new Employee("Richard Potter", "E43128", 15900, "D101"),
new Employee("David Motsinger", "E27002", 19250, "D202"),
new Employee("Tim Sampair", "E03033", 27000, "D101"),
new Employee("Kim Arlich", "E10001", 57000, "D190"),
new Employee("Timothy Grove", "E16398", 29900, "D190")
};
 
DisplayTopNPerDepartment(employees, 2);
}
 
static void DisplayTopNPerDepartment(IEnumerable<Employee> employees, int n)
{
var topSalariesByDepartment =
from employee in employees
group employee by employee.Department
into g
select new
{
Department = g.Key,
TopEmployeesBySalary = g.OrderByDescending(e => e.Salary).Take(n)
};
 
foreach (var x in topSalariesByDepartment)
{
Console.WriteLine("Department: " + x.Department);
foreach (var employee in x.TopEmployeesBySalary)
Console.WriteLine(employee);
Console.WriteLine("----------------------------");
}
}
}</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">
Department: D101
George Woltman E21437 53500
David McClellan E04242 41500
----------------------------
Department: D050
John Rappl E21437 47000
Nathan Adams E41298 21900
----------------------------
Department: D202
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
----------------------------
Department: D190
Kim Arlich E10001 57000
Timothy Grove E16398 29900
----------------------------</pre>
 
Online demo: http://ideone.com/95TxAV
 
=={{header|C++}}==
<langsyntaxhighlight lang="cpp">#include <string>
#include <set>
#include <list>
Line 265 ⟶ 1,047:
struct Employee
{
std::string Name;
std::string ID;
unsigned long Salary;
std::string Department;
Employee(std::string _Name = "", std::string _ID = "", unsigned long _Salary = 0, std::string _Department = "")
: Name(_Name), ID(_ID), Salary(_Salary), Department(_Department)
{ }
 
void display(std::ostream& out) const
{
{
out << Name << "\t" << ID << "\t" << Salary << "\t" << Department << std::endl;
}
}
};
 
Line 282 ⟶ 1,064:
struct CompareEarners
{
bool operator()(const Employee& e1, const Employee& e2)
{
{
return (e1.Salary > e2.Salary);
}
}
};
 
Line 299 ⟶ 1,081:
void initialize(EMPLOYEELIST& Employees)
{
// Initialize our employee list data source.
Employees.push_back(Employee("Tyler Bennett", "E10297", 32000, "D101"));
Employees.push_back(Employee("John Rappl", "E21437", 47000, "D050"));
Employees.push_back(Employee("George Woltman", "E21437", 53500, "D101"));
Employees.push_back(Employee("Adam Smith", "E21437", 18000, "D202"));
Employees.push_back(Employee("Claire Buckman", "E39876", 27800, "D202"));
Employees.push_back(Employee("David McClellan", "E04242", 41500, "D101"));
Employees.push_back(Employee("Rich Holcomb", "E01234", 49500, "D202"));
Employees.push_back(Employee("Nathan Adams", "E41298", 21900, "D050"));
Employees.push_back(Employee("Richard Potter", "E43128", 15900, "D101"));
Employees.push_back(Employee("David Motsinger", "E27002", 19250, "D202"));
Employees.push_back(Employee("Tim Sampair", "E03033", 27000, "D101"));
Employees.push_back(Employee("Kim Arlich", "E10001", 57000, "D190"));
Employees.push_back(Employee("Timothy Grove", "E16398", 29900, "D190"));
}
 
void group(EMPLOYEELIST& Employees, DEPARTMENTLIST& Departments)
{
// Loop through all of our employees.
for( EMPLOYEELIST::iterator iEmployee = Employees.begin();
Employees.end() != iEmployee;
++iEmployee )
{
{
DEPARTMENTPAYROLL& groupSet = Departments[iEmployee->Department];
 
// Add our employee to this group.
groupSet.insert(*iEmployee);
}
}
}
 
void present(DEPARTMENTLIST& Departments, unsigned int N)
{
// Loop through all of our departments
for( DEPARTMENTLIST::iterator iDepartment = Departments.begin();
Departments.end() != iDepartment;
++iDepartment )
{
{
std::cout << "In department " << iDepartment->first << std::endl;
std::cout << "Name\t\tID\tSalary\tDepartment" << std::endl;
// Get the top three employees for each employee
unsigned int rank = 1;
for( DEPARTMENTPAYROLL::iterator iEmployee = iDepartment->second.begin();
( iDepartment->second.end() != iEmployee) && (rank <= N);
++iEmployee, ++rank )
{
{
iEmployee->display(std::cout);
}
}
std::cout << std::endl;
}
}
}
 
int main(int argc, char* argv[])
{
// Our container for our list of employees.
EMPLOYEELIST Employees;
 
// Fill our list of employees
initialize(Employees);
 
// Our departments.
DEPARTMENTLIST Departments;
 
// Sort our employees into their departments.
// This will also rank them.
group(Employees, Departments);
 
// Display the top 3 earners in each department.
present(Departments, 3);
 
return 0;
}</syntaxhighlight>{{out}}
}</lang>
<pre style="height:30ex;overflow:scroll">In department D050
 
Output:
<pre>In department D050
Name ID Salary Department
John Rappl E21437 47000 D050
Line 394 ⟶ 1,174:
David Motsinger E27002 19250 D202</pre>
 
=={{header|C sharp|C#Ceylon}}==
<syntaxhighlight lang="ceylon">class Employee(name, id, salary, dept) {
{{Incorrect|C#|The code does not accept ''N'' as a parameter anywhere.}}
shared String name;
shared String id;
shared Integer salary;
shared String dept;
 
string => "``name`` ``id`` $``salary``.00 ``dept``";
<lang csharp>using System;
}
using System.Collections.Generic;
using System.Linq;
 
Employee[] employees = [
class Program
Employee("Tyler Bennett", "E10297", 32000, "D101"),
{
class Employee("John Rappl", "E21437", 47000, "D050"),
Employee("George Woltman", "E00127", 53500, "D101"),
{
public Employee(string"Adam nameSmith", string id"E63535", int salary18000, string department"D202"),
Employee("Claire Buckman", "E39876", 27800, "D202"),
{
Employee("David McClellan", "E04242", 41500, "D101"),
Name = name;
Employee("Rich Holcomb", "E01234", 49500, "D202"),
ID = id;
Employee("Nathan Adams", "E41298", 21900, "D050"),
Salary = salary;
Employee("Richard Potter", "E43128", 15900, "D101"),
Department = department;
Employee("David Motsinger", "E27002", 19250, "D202"),
}
Employee("Tim Sampair", "E03033", 27000, "D101"),
Employee("Kim Arlich", "E10001", 57000, "D190"),
Employee("Timothy Grove", "E16398", 29900, "D190")
];
 
"This is the main function."
public string Name;
shared void run() {
public string ID;
public int Salary;
public string Department;
}
 
value topRanked = topSalaries(employees, 3);
static void Main(string[] args)
{
var Employees = new List<Employee>
{
new Employee("Tyler Bennett", "E10297", 32000, "D101"),
new Employee("John Rappl", "E21437", 47000, "D050"),
new Employee("George Woltman", "E21437", 53500, "D101"),
new Employee("Adam Smith", "E21437", 18000, "D202"),
new Employee("Claire Buckman", "E39876", 27800, "D202"),
new Employee("David McClellan", "E04242", 41500, "D101"),
new Employee("Rich Holcomb", "E01234", 49500, "D202"),
new Employee("Nathan Adams", "E41298", 21900, "D050"),
new Employee("Richard Potter", "E43128", 15900, "D101"),
new Employee("David Motsinger", "E27002", 19250, "D202"),
new Employee("Tim Sampair", "E03033", 27000, "D101"),
new Employee("Kim Arlich", "E10001", 57000, "D190"),
new Employee("Timothy Grove", "E16398", 29900, "D190")
};
 
for (dept -> staff in topRanked) {
var rankedGroups = from e in Employees
print(dept);
orderby e.Department, e.Salary descending
for (employee in staff) {
group e by e.Department into g
print("\t``employee``");
select new { Dept = g.Key, EmpDetails = g };
 
foreach (var group in rankedGroups)
{
Console.WriteLine("Department {0,-11}\r\n---------------", group.Dept);
Console.WriteLine(" Name ID Salary Department\r\n --------------- ------ ------ ----------");
 
foreach (Employee e in group.EmpDetails)
Console.WriteLine(" {0, -15} {1} {2} {3}", e.Name, e.ID, e.Salary, e.Department);
 
Console.WriteLine();
}
}
}
}</lang>
 
Map<String, {Employee*}> topSalaries({Employee*} employees, Integer n) => map {
Output:
for (dept -> staff in employees.group(Employee.dept))
<pre>Department D050
dept -> staff.sort(byDecreasing(Employee.salary)).take(n)
---------------
};</syntaxhighlight>
Name ID Salary Department
--------------- ------ ------ ----------
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
 
Department D101
---------------
Name ID Salary Department
--------------- ------ ------ ----------
George Woltman E21437 53500 D101
David McClellan E04242 41500 D101
Tyler Bennett E10297 32000 D101
Tim Sampair E03033 27000 D101
Richard Potter E43128 15900 D101
 
Department D190
---------------
Name ID Salary Department
--------------- ------ ------ ----------
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
 
Department D202
---------------
Name ID Salary Department
--------------- ------ ------ ----------
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
David Motsinger E27002 19250 D202
Adam Smith E21437 18000 D202</pre>
 
=={{header|Clojure}}==
 
<syntaxhighlight lang="lisp">(use '[clojure.contrib.seq-utils :only (group-by)])
<lang lisp>
(use '[clojure.contrib.seq-utils :only (group-by)])
 
(defstruct employee :Name :ID :Salary :Department)
Line 518 ⟶ 1,245:
(doseq [e (take 3 (reverse (sort-by :Salary emps)))]
(println e)))
</syntaxhighlight>{{out}}
</lang>
<pre style="height:30ex;overflow:scroll">Department: D050
 
Output:
 
<pre>
Department: D050
{:Name John Rappl, :ID E21437, :Salary 47000, :Department D050}
{:Name Nathan Adams, :ID E41298, :Salary 21900, :Department D050}
Line 540 ⟶ 1,263:
 
=={{header|Common Lisp}}==
<langsyntaxhighlight lang="lisp">(defun top-n-by-group (n data value-key group-key predicate &key (group-test 'eql))
(let ((not-pred (complement predicate))
(group-data (make-hash-table :test group-test)))
Line 573 ⟶ 1,296:
(second entry) (insert datum (second entry))))))
(dolist (datum data group-data)
(update-entry (entry (funcall group-key datum)) datum)))))</langsyntaxhighlight>
 
Example
<syntaxhighlight lang="lisp">> (defparameter *employee-data*
 
<lang lisp>> (defparameter *employee-data*
'(("Tyler Bennett" E10297 32000 D101)
("John Rappl" E21437 47000 D050)
Line 604 ⟶ 1,325:
D050 (2 (("Nathan Adams" E41298 21900 D050) ("John Rappl" E21437 47000 D050)))
D202 (3 (("David Motsinger" E27002 19250 D202) ("Claire Buckman" E39876 27800 D202) ("Rich Holcomb" E01234 49500 D202)))
D190 (2 (("Timothy Grove" E16398 29900 D190) ("Kim Arlich" E10001 57000 D190)))</langsyntaxhighlight>
 
=={{header|D}}==
<langsyntaxhighlight lang="d">import std.stdio, std.algorithm, std.conv, std.range;
 
struct Employee {
string name, id;
intuint salary;
string department;
 
int opCmp(in ref typeof(this) other) pure nothrow {
return other.salary - this.salary;
}
}
 
immutable Employee[] data = [
{"Tyler Bennett", "E10297", 3200032_000, "D101"},
{"John Rappl", "E21437", 4700047_000, "D050"},
{"George Woltman", "E00127", 5350053_500, "D101"},
{"Adam Smith", "E63535", 1800018_000, "D202"},
{"Claire Buckman", "E39876", 2780027_800, "D202"},
{"David McClellan", "E04242", 4150041_500, "D101"},
{"Rich Holcomb", "E01234", 4950049_500, "D202"},
{"Nathan Adams", "E41298", 2190021_900, "D050"},
{"Richard Potter", "E43128", 1590015_900, "D101"},
{"David Motsinger", "E27002", 1925019_250, "D202"},
{"Tim Sampair", "E03033", 2700027_000, "D101"},
{"Kim Arlich", "E10001", 5700057_000, "D190"},
{"Timothy Grove", "E16398", 2990029_900, "D190"}];
 
void main(in string[] args) {
immutable Nn = (args.length == 2) ? to!int(args[1]) : 3;
 
Employee[][string] departments;
foreach (immutable rec; data)
departments[rec.department] ~= rec;
 
foreach (departmentdep, recs; departments) {
recs.topN!q{a.salary > b.salary}(n);
writeln("Department ", department);
writefln("Department %s\n %(%s\n %)\n", dep, recs.take(n));
writeln(" Employee Name Employee ID Salary Department");
topN(recs, N);
foreach (r; recs[0 .. min($, N)])
writefln(" %-15s %-15s %-15s %-15s",
r.name, r.id, r.salary, r.department);
writeln();
}
}</syntaxhighlight>{{out}}
}</lang>
<pre style="height:30ex;overflow:scroll">Department D202
Output:
Employee("Rich Holcomb", "E01234", 49500, "D202")
<pre>Department D202
Employee("Claire Buckman", "E39876", 27800, "D202")
Employee Name Employee ID Salary Department
Employee("David Motsinger", "E27002", 19250, "D202")
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
David Motsinger E27002 19250 D202
 
Department D190
Employee("Kim Arlich", "E10001", 57000, "D190")
Employee Name Employee ID Salary Department
Employee("Timothy Grove", "E16398", 29900, "D190")
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
 
Department D101
Employee("George Woltman", "E00127", 53500, "D101")
Employee Name Employee ID Salary Department
Employee("David McClellan", "E04242", 41500, "D101")
George Woltman E00127 53500 D101
Employee("Tyler Bennett", "E10297", 32000, "D101")
David McClellan E04242 41500 D101
Tyler Bennett E10297 32000 D101
 
Department D050
Employee("John Rappl", "E21437", 47000, "D050")
Employee Name Employee ID Salary Department
Employee("Nathan Adams", "E41298", 21900, "D050")</pre>
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050</pre>
 
=={{header|EDelphi}}==
{{works with|Delphi|6.0}}
{{libheader|SysUtils,StdCtrls}}
 
 
<lang e>/** Turn a list of arrays into a list of maps with the given keys. */
<syntaxhighlight lang="Delphi">
 
type TEmployeeInfo = record
Name,ID: string;
Salary: double;
Dept: string;
end;
 
var EmployeeInfo: array [0..12] of TEmployeeInfo = (
(Name: 'Tyler Bennett'; ID: 'E10297'; Salary: 32000; Dept: 'D101'),
(Name: 'John Rappl'; ID: 'E21437'; Salary: 47000; Dept: 'D050'),
(Name: 'George Woltman'; ID: 'E00127'; Salary: 53500; Dept: 'D101'),
(Name: 'Adam Smith'; ID: 'E63535'; Salary: 18000; Dept: 'D202'),
(Name: 'Claire Buckman'; ID: 'E39876'; Salary: 27800; Dept: 'D202'),
(Name: 'David McClellan'; ID: 'E04242'; Salary: 41500; Dept: 'D101'),
(Name: 'Rich Holcomb'; ID: 'E01234'; Salary: 49500; Dept: 'D202'),
(Name: 'Nathan Adams'; ID: 'E41298'; Salary: 21900; Dept: 'D050'),
(Name: 'Richard Potter'; ID: 'E43128'; Salary: 15900; Dept: 'D101'),
(Name: 'David Motsinger'; ID: 'E27002'; Salary: 19250; Dept: 'D202'),
(Name: 'Tim Sampair'; ID: 'E03033'; Salary: 27000; Dept: 'D101'),
(Name: 'Kim Arlich'; ID: 'E10001'; Salary: 57000; Dept: 'D190'),
(Name: 'Timothy Grove'; ID: 'E16398'; Salary: 29900; Dept: 'D190')
);
 
 
function SalarySort(Item1, Item2: Pointer): Integer;
var Info1,Info2: TEmployeeInfo;
begin
Info1:=TEmployeeInfo(Item1^);
Info2:=TEmployeeInfo(Item2^);
Result:=AnsiCompareStr(Info1.Dept,Info2.Dept);
If Result=0 then Result:=Trunc(Info1.Salary-Info2.Salary);
end;
 
procedure ShowTopSalaries(Memo: TMemo);
var List: TList;
var Info: TEmployeeInfo;
var I: integer;
var S,OldDept: string;
 
procedure NewDepartment(Name: string);
begin
Memo.Lines.Add('');
Memo.Lines.Add('Department: '+Name);
Memo.Lines.Add('Employee Name Employee ID Salary Department');
OldDept:=Name;
end;
 
 
begin
List:=TList.Create;
try
for I:=0 to High(EmployeeInfo) do
List.Add(@EmployeeInfo[I]);
List.Sort(SalarySort);
OldDept:='';
for I:=0 to List.Count-1 do
begin
Info:=TEmployeeInfo(List[I]^);
if OldDept<>Info.Dept then NewDepartment(Info.Dept);
S:=Format('%-18S %9S %11.0m %8S',[Info.Name,Info.ID,Info.Salary,Info.Dept]);
Memo.Lines.Add(S);
end;
 
finally List.Free; end;
end;
 
 
</syntaxhighlight>
{{out}}
<pre>
Department: D050
Employee Name Employee ID Salary Dept
Nathan Adams E41298 $21,900 D050
John Rappl E21437 $47,000 D050
 
Department: D101
Employee Name Employee ID Salary Dept
Richard Potter E43128 $15,900 D101
Tim Sampair E03033 $27,000 D101
Tyler Bennett E10297 $32,000 D101
David McClellan E04242 $41,500 D101
George Woltman E00127 $53,500 D101
 
Department: D190
Employee Name Employee ID Salary Dept
Timothy Grove E16398 $29,900 D190
Kim Arlich E10001 $57,000 D190
 
Department: D202
Employee Name Employee ID Salary Dept
Adam Smith E63535 $18,000 D202
David Motsinger E27002 $19,250 D202
Claire Buckman E39876 $27,800 D202
Rich Holcomb E01234 $49,500 D202
 
Elapsed Time: 35.284 ms.
</pre>
 
=={{header|Dyalect}}==
 
<syntaxhighlight lang="dyalect">type Employee(name,id,salary,department) with Lookup
func Employee.ToString() {
"$\(this.salary) (name: \(this.name), id: \(this.id), department: \(this.department)"
}
let employees = [
Employee("Tyler Bennett","E10297",32000,"D101"),
Employee("John Rappl","E21437",47000,"D050"),
Employee("George Woltman","E00127",53500,"D101"),
Employee("Adam Smith","E63535",18000,"D202"),
Employee("Claire Buckman","E39876",27800,"D202"),
Employee("David McClellan","E04242",41500,"D101"),
Employee("Rich Holcomb","E01234",49500,"D202"),
Employee("Nathan Adams","E41298",21900,"D050"),
Employee("Richard Potter","E43128",15900,"D101"),
Employee("David Motsinger","E27002",19250,"D202"),
Employee("Tim Sampair","E03033",27000,"D101"),
Employee("Kim Arlich","E10001",57000,"D190"),
Employee("Timothy Grove","E16398",29900,"D190")
]
func topNSalaries(n) {
//We sort employees based on salary
employees.Sort((x,y) => y.salary - x.salary)
let max =
if n > employees.Length() - 1 {
employees.Length() - 1
} else {
n
}
for i in 0..max {
yield employees[i]
}
}
var seq = topNSalaries(5)
for e in seq {
print(e)
}</syntaxhighlight>
 
{{out}}
 
<pre>$57000 (name: Kim Arlich, id: E10001, department: D190
$53500 (name: George Woltman, id: E00127, department: D101
$49500 (name: Rich Holcomb, id: E01234, department: D202
$47000 (name: John Rappl, id: E21437, department: D050
$41500 (name: David McClellan, id: E04242, department: D101
$32000 (name: Tyler Bennett, id: E10297, department: D101</pre>
 
=={{header|E}}==
<syntaxhighlight lang="e">/** Turn a list of arrays into a list of maps with the given keys. */
def addKeys(keys, rows) {
def res := [].diverge()
Line 713 ⟶ 1,577:
out.println()
}
}</langsyntaxhighlight>
 
(Note: This uses an append-and-then-sort to maintain the list of top N; a sorted insert or a proper [[wp: selection algorithm|selection algorithm]] would be more efficient. As long as <var>N</var> is small, this does not matter much; the algorithm is O(n) with respect to the data set.)
 
Note: This uses an append-and-then-sort to maintain the list of top N; a sorted insert or a proper [[wp: selection algorithm|selection algorithm]] would be more efficient. As long as <var>N</var> is small, this does not matter much; the algorithm is O(n) with respect to the data set.{{out}}
<lang e>? topSalaries(3, stdout)
<pre style="height:30ex;overflow:scroll">? topSalaries(3, stdout)
Department D050
---------------
Line 738 ⟶ 1,601:
E01234 $49500 Rich Holcomb
E39876 $27800 Claire Buckman
E27002 $19250 David Motsinger</langpre>
 
=={{header|EchoLisp}}==
We use the '''sql.lib''' to select, sort and group records from the table 'emps'. The output is appended in a new table, 'high'.
<syntaxhighlight lang="scheme">
(lib 'struct) ;; tables are based upon structures
(lib 'sql) ;; sql-select function
 
;; input table
(define emps (make-table (struct emp (name id salary dept))))
;; output table
(define high (make-table (struct out (dept name salary))))
 
;; sort/group procedure
(define (get-high num-records: N into: high)
(sql-select emp.dept emp.name emp.salary
from emps
group-by emp.dept
order-by emp.salary desc limit N into high))
</syntaxhighlight>
{{out}}
<syntaxhighlight lang="scheme">
(define emps_file
'(("Tyler Bennett" E10297 32000 D101)
("John Rappl" E21437 47000 D050)
("George Woltman" E00127 53500 D101)
("Adam Smith" E63535 18000 D202)
("Claire Buckman" E39876 27800 D202)
("David McClellan" E04242 41500 D101)
("Rich Holcomb" E01234 49500 D202)
("Simon Gallubert" E00000 42 D666)
("Nathan Adams" E41298 21900 D050)
("Richard Potter" E43128 15900 D101)
("David Motsinger" E27002 19250 D202)
("Tim Sampair" E03033 27000 D101)
("Kim Arlich" E10001 57000 D190)
("Timothy Grove" E16398 29900 D190)))
 
(list->table emps_file emps ) ;; load the table
 
(get-high 2 high)
(table-print high)
 
[0] D050 John Rappl 47000
[1] D050 Nathan Adams 21900
[2] D101 George Woltman 53500
[3] D101 David McClellan 41500
[4] D190 Kim Arlich 57000
[5] D190 Timothy Grove 29900
[6] D202 Rich Holcomb 49500
[7] D202 Claire Buckman 27800
[8] D666 Simon Gallubert 42
 
(sql-delete from high)
(get-high 1 high)
(table-print high)
[0] D050 John Rappl 47000
[1] D101 George Woltman 53500
[2] D190 Kim Arlich 57000
[3] D202 Rich Holcomb 49500
[4] D666 Simon Gallubert 42
</syntaxhighlight>
 
=={{header|Elena}}==
ELENA 6.x :
<syntaxhighlight lang="elena">import system'collections;
import system'routines;
import extensions;
import extensions'routines;
import extensions'text;
class Employee
{
string Name : prop;
string ID : prop;
int Salary : prop;
string Department : prop;
string toPrintable()
= new StringWriter()
.writePaddingRight(Name, 25)
.writePaddingRight(ID, 12)
.writePaddingRight(Salary.toPrintable(), 12)
.write(Department);
}
extension reportOp
{
topNPerDepartment(n)
= self.groupBy::(x => x.Department ).selectBy::(x)
{
^ new {
Department = x.Key;
Employees
= x.orderBy::(f,l => f.Salary > l.Salary ).top(n).summarize(new ArrayList());
}
};
}
public program()
{
var employees := new Employee[]
{
new Employee{ this Name := "Tyler Bennett"; this ID := "E10297"; this Salary:=32000; this Department:="D101";},
new Employee{ this Name := "John Rappl"; this ID := "E21437"; this Salary:=47000; this Department:="D050";},
new Employee{ this Name := "George Woltman"; this ID := "E00127"; this Salary:=53500; this Department:="D101";},
new Employee{ this Name := "Adam Smith"; this ID := "E63535"; this Salary:=18000; this Department:="D202";},
new Employee{ this Name := "Claire Buckman"; this ID := "E39876"; this Salary:=27800; this Department:="D202";},
new Employee{ this Name := "David McClellan"; this ID := "E04242"; this Salary:=41500; this Department:="D101";},
new Employee{ this Name := "Rich Holcomb"; this ID := "E01234"; this Salary:=49500; this Department:="D202";},
new Employee{ this Name := "Nathan Adams"; this ID := "E41298"; this Salary:=21900; this Department:="D050";},
new Employee{ this Name := "Richard Potter"; this ID := "E43128"; this Salary:=15900; this Department:="D101";},
new Employee{ this Name := "David Motsinger"; this ID := "E27002"; this Salary:=19250; this Department:="D202";},
new Employee{ this Name := "Tim Sampair"; this ID := "E03033"; this Salary:=27000; this Department:="D101";},
new Employee{ this Name := "Kim Arlich"; this ID := "E10001"; this Salary:=57000; this Department:="D190";},
new Employee{ this Name := "Timothy Grove"; this ID := "E16398"; this Salary:=29900; this Department:="D190";}
};
employees.topNPerDepartment(2).forEach::(info)
{
console.printLine("Department: ",info.Department);
info.Employees.forEach(printingLn);
console.writeLine("---------------------------------------------")
};
console.readChar()
}</syntaxhighlight>
{{out}}
<pre>
Department: D101
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
---------------------------------------------
Department: D050
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
---------------------------------------------
Department: D202
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
---------------------------------------------
Department: D190
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
---------------------------------------------
</pre>
 
=={{header|Elixir}}==
Quick implementation using piping and anonymous functions.
<syntaxhighlight lang="elixir">defmodule TopRank do
def per_groupe(data, n) do
String.split(data, ~r/(\n|\r\n|\r)/, trim: true)
|> Enum.drop(1)
|> Enum.map(fn person -> String.split(person,",") end)
|> Enum.group_by(fn person -> department(person) end)
|> Enum.each(fn {department,group} ->
IO.puts "Department: #{department}"
Enum.sort_by(group, fn person -> -salary(person) end)
|> Enum.take(n)
|> Enum.each(fn person -> IO.puts str_format(person) end)
end)
end
defp salary([_,_,x,_]), do: String.to_integer(x)
defp department([_,_,_,x]), do: x
defp str_format([a,b,c,_]), do: " #{a} - #{b} - #{c} annual salary"
end
 
data = """
Employee Name,Employee ID,Salary,Department
Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190
"""
TopRank.per_groupe(data, 3)</syntaxhighlight>
 
{{out}}
<pre>
Department: D050
John Rappl - E21437 - 47000 annual salary
Nathan Adams - E41298 - 21900 annual salary
Department: D101
George Woltman - E00127 - 53500 annual salary
David McClellan - E04242 - 41500 annual salary
Tyler Bennett - E10297 - 32000 annual salary
Department: D190
Kim Arlich - E10001 - 57000 annual salary
Timothy Grove - E16398 - 29900 annual salary
Department: D202
Rich Holcomb - E01234 - 49500 annual salary
Claire Buckman - E39876 - 27800 annual salary
David Motsinger - E27002 - 19250 annual salary
</pre>
 
=={{header|Erlang}}==
<syntaxhighlight lang="erlang"><-module( top_rank_per_group ).
 
-export( [employees/0, employees_in_department/2, highest_payed/2, task/1] ).
 
-record( employee, {name, id, salery, department} ).
 
employees() ->
[#employee{name="Tyler Bennett", id="E10297", salery=32000, department="D101"},
#employee{name="John Rappl", id="E21437", salery=47000, department="D101"},
#employee{name="George Woltman", id="E00127", salery=53500, department="D050"},
#employee{name="Adam Smith", id="E63535", salery=18000, department="D202"},
#employee{name="Claire Buckman", id="E39876", salery=27800, department="D202"},
#employee{name="David McClellan", id="E04242", salery=41500, department="D101"},
#employee{name="Rich Holcomb", id="E01234", salery=49500, department="D202"},
#employee{name="Nathan Adams", id="E41298", salery=21900, department="D050"},
#employee{name="Richard Potter", id="E43128", salery=15900, department="D101"},
#employee{name="David Motsinger", id="E27002", salery=19250, department="D202"},
#employee{name="Tim Sampair", id="E03033", salery=27000, department="D101"},
#employee{name="Kim Arlich", id="E10001", salery=57000, department="D190"},
#employee{name="Timothy Grove", id="E16398", salery=29900, department="D190"}].
 
employees_in_department( Department, Employees ) -> [X || #employee{department=D}=X <- Employees, D =:= Department].
 
highest_payed( N, Employees ) ->
{Highest, _T} = lists:split( N, lists:reverse(lists:keysort(#employee.salery, Employees)) ),
Highest.
 
task( N ) ->
Employees = employees(),
Departments = lists:usort( [X || #employee{department=X} <- Employees] ),
Employees_in_departments = [employees_in_department(X, Employees) || X <- Departments],
Highest_payed_in_departments = [highest_payed(N, Xs) || Xs <- Employees_in_departments],
[task_write(X) || X <- Highest_payed_in_departments].
 
 
 
task_write( Highest_payeds ) ->
[io:fwrite( "~p ~p: ~p~n", [Department, Salery, Name]) || #employee{department=Department, salery=Salery, name=Name} <- Highest_payeds],
io:nl().
</syntaxhighlight>{{out}}
<pre><14> top_rank_per_group:task(2).
"D050" 53500: "George Woltman"
"D050" 21900: "Nathan Adams"
 
"D101" 47000: "John Rappl"
"D101" 41500: "David McClellan"
 
"D190" 57000: "Kim Arlich"
"D190" 29900: "Timothy Grove"
 
"D202" 49500: "Rich Holcomb"
"D202" 27800: "Claire Buckman"</pre>
 
=={{header|F Sharp|F#}}==
<langsyntaxhighlight lang="fsharp">let data =
[
"Tyler Bennett", "E10297", 32000, "D101";
Line 760 ⟶ 1,882:
let topRank n =
Seq.groupBy (fun (_, _, _, d) -> d) data
|> Seq.map (snd >> Seq.sortBy (fun (_, _, s, _) -> -s) >> Seq.take n)</langsyntaxhighlight>
 
=={{header|Factor}}==
<langsyntaxhighlight lang="factor">USING: accessors assocs fry io kernel math.parser sequences
sorting ;
IN: top-rank
Line 804 ⟶ 1,926:
] first-n-each
nl
] assoc-each ;</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">
Output:
<pre>
Department D101:
E00127 $53500 George Woltman
Line 825 ⟶ 1,946:
E41298 $21900 Nathan Adams
</pre>
 
=={{header|Forth}}==
<syntaxhighlight lang="forth">
\ Written in ANS-Forth; tested under VFX.
\ Requires the novice package: http://www.forth.org/novice.html
\ The following should already be done:
\ include novice.4th
\ include list.4th
 
marker TopRank.4th
 
\ This demonstrates how I typically use lists. A program such as this does not need any explicit iteration, so it is more like Factor than C.
\ I would define high-level languages as those that allow programs to be written without explicit iteration. Iteration is a major source of bugs.
\ The C library has QSORT that hides iteration, but user-written code very rarely uses this technique, and doesn't in the TopRank example.
 
 
\ ******
\ ****** The following defines our data-structure.
\ ****** Pretty much every struct definition has these basic functions.
\ ******
 
list
w field .name \ string
w field .id \ string
w field .salary \ integer
w field .dept \ string
constant employee
 
: init-employee ( name id salary department node -- node )
init-list >r
hstr r@ .dept !
r@ .salary !
hstr r@ .id !
hstr r@ .name !
r> ;
: new-employee ( name id salary dept -- node )
employee alloc
init-employee ;
: <kill-employee> ( node -- )
dup .name @ dealloc
dup .id @ dealloc
dup .dept @ dealloc
dealloc ;
 
: kill-employee ( head -- )
each[ <kill-employee> ]each ;
: <clone-employee> ( node -- new-node )
clone-node
dup .name @ hstr over .name !
dup .id @ hstr over .id !
dup .dept @ hstr over .dept ! ;
: clone-employee ( head -- new-head )
nil
swap each[ <clone-employee> link ]each ;
 
: <show-employee> ( node -- )
dup .id @ count type 4 spaces
dup .dept @ count type 4 spaces
dup .salary @ . 4 spaces
dup .name @ count type cr
drop ;
: show-employee ( head -- )
cr
each[ <show-employee> ]each ;
 
\ ******
\ ****** The following code is specific to the query that we want to do in this example problem.
\ ******
 
: employee-dept-salary ( new-node node -- new-node ? ) \ for use by FIND-PRIOR or INSERT-ORDERED
2dup
.dept @ count rot .dept @ count compare ?dup if A>B = nip exit then
.salary @ over .salary @ < ;
 
: <top> ( n rank current-dept head node -- n rank current-dept head )
2>r \ -- n rank current-dept
dup count r@ .dept @ count compare A=B <> if \ we have a new department
2drop \ discard RANK and CURRENT-DEPT
0 r@ .dept @ then \ start again with a new RANK and CURRENT-DEPT
rover rover > if \ if N > RANK then it is good
r> r> over \ -- n rank current-dept node head node
<clone-employee> link \ -- n rank current-dept node head
>r >r then \ -- n rank current-dept
swap 1+ swap \ increment RANK
rdrop r> ; \ -- n rank current-dept head
: top ( n head -- new-head ) \ make a new list of the top N salary earners in each dept \ requires that list be sorted by dept-salary
>r
0 c" xxx" nil \ -- n rank current-dept new-head \ initially for an invalid department
r> ['] <top> each
3nip ;
\ ******
\ ****** The following is a test of the program using sample data.
\ ******
nil ' employee-dept-salary
c" Tyler Bennett" c" E10297" 32000 c" D101" new-employee insert-ordered
c" John Rappl" c" E21437" 47000 c" D050" new-employee insert-ordered
c" George Woltman" c" E00127" 53500 c" D101" new-employee insert-ordered
c" Adam Smith" c" E63535" 18000 c" D202" new-employee insert-ordered
c" Claire Buckman" c" E39876" 27800 c" D202" new-employee insert-ordered
c" David McClellan" c" E04242" 41500 c" D101" new-employee insert-ordered
c" Rich Holcomb" c" E01234" 49500 c" D202" new-employee insert-ordered
c" Nathan Adams" c" E41298" 21900 c" D050" new-employee insert-ordered
c" Richard Potter" c" E43128" 15900 c" D101" new-employee insert-ordered
c" David Motsinger" c" E27002" 19250 c" D202" new-employee insert-ordered
c" Tim Sampair" c" E03033" 27000 c" D101" new-employee insert-ordered
c" Kim Arlich" c" E10001" 57000 c" D190" new-employee insert-ordered
c" Timothy Grove" c" E16398" 29900 c" D190" new-employee insert-ordered
drop constant test-data
 
cr .( N = 0 )
0 test-data top dup show-employee kill-employee
 
cr .( N = 1 )
1 test-data top dup show-employee kill-employee
 
cr .( N = 2 )
2 test-data top dup show-employee kill-employee
 
cr .( N = 3 )
3 test-data top dup show-employee kill-employee
 
test-data kill-employee
</syntaxhighlight>
{{out}}
<pre style="height:30ex;overflow:scroll">
N = 0
 
N = 1
E21437 D050 47000 John Rappl
E00127 D101 53500 George Woltman
E10001 D190 57000 Kim Arlich
E01234 D202 49500 Rich Holcomb
 
N = 2
E21437 D050 47000 John Rappl
E41298 D050 21900 Nathan Adams
E00127 D101 53500 George Woltman
E04242 D101 41500 David McClellan
E10001 D190 57000 Kim Arlich
E16398 D190 29900 Timothy Grove
E01234 D202 49500 Rich Holcomb
E39876 D202 27800 Claire Buckman
 
N = 3
E21437 D050 47000 John Rappl
E41298 D050 21900 Nathan Adams
E00127 D101 53500 George Woltman
E04242 D101 41500 David McClellan
E10297 D101 32000 Tyler Bennett
E10001 D190 57000 Kim Arlich
E16398 D190 29900 Timothy Grove
E01234 D202 49500 Rich Holcomb
E39876 D202 27800 Claire Buckman
E27002 D202 19250 David Motsinger
</pre>
 
=={{header|Fortran}}==
The example data can easily be declared via DATA statements, as with <syntaxhighlight lang="fortran"> DATA EMPLOYEE(1:3)/
1 GRIST("Tyler Bennett","E10297",32000,"D101"),
2 GRIST("John Rappl","E21437",47000,"D050"),
3 GRIST("George Woltman","E00127",53500,"D101")/
</syntaxhighlight>
(just showing the first three), however this does not allow the preparation of the header line, because <code>GRIST("Employee Name","Employee ID","Salary","Department")</code> would be invalid since the "salary" entry is a floating-point number in the data aggregate, not text. The header line could be discarded, but this is in violation of the ideas of properly-described data files. So, the plan is to read the data from an input file containing the full example input, with its header line given special treatment. No attempt is made to detect or report improperly-formatted data. The READ statements could refer to an internally-defined block of text, but reading an actual disc file is more normal. Ad-hoc decisions are made for the size of the CHARACTER variables, that are "surely big enough" for the example data, likewise with the size of the array to hold all the records.
 
For simplicity, and following the specification to not parse the data at runtime but to use built-in facilities, free-format input is intended. This does not require text variables to be quoted, however, because ''both'' a comma ''and'' a space are accepted as delimiters the specified data cannot be read as provided since some text fields contain a space. Such text fields must be placed within quotes to avoid missteps. This of course must also be done if a DATA statement as above were to be employed. On the other hand, the example data contains only names with two parts separated by a space - and this could be relied on as a separator to read a two-part name variable. Similarly, all the salary values have the same number of digits and so could be sorted as text variables to give the desired numerical order. The resulting scheme will be easily disrupted by data other than those of the example! I recall working at the NZ Post Office in the 1970s when there came a time that higher-paid staff achieved fortnightly pays in excess of $999·99 and the computer system couldn't handle it. Similarly, I have a three-part name. Asian names are often written with family first, and there should be a comma when this ordering is used in English: Bloggs, Joe; Kim, Il-sung; Kim, Jong-il; Kim, Jong-un. To have such name texts read as one variable would definitely require them to be enclosed in quotes so that their included comma is not taken as a delimiter in free-format input, but a more likely usage would be to read two fields and argue over two-part names only. Some Asian names are one-part, even as a formal usage. The question is whether the names part is a "block" of text to be shown as-is, or whether it is to have components.
 
The only remaining requirement is that the type of datum being read matches the type of the corresponding variable in the ''input-list'' that is to receive that datum; happily this is the case as each record has four fields, always in the same order and the same type. (I have got nowhere explaining to a data supplier that the header line can use different names for the columns if they are always in the same order, or, the columns can be in a different order provided that their column names are always the same - but varying both the names and the order forces me to personally inspect the data file and make guesses. Bah.) Except for the header line, which has four character fields and so can be read into a CHARACTER array, as ever of a "suitable" size. There is alas no facility whereby, ''during the input processing itself'' the receiving variable can be resized to suit the actual size of its incoming datum, as it is being read. Fortran array indexing is done by calculating storage locations employing known and fixed size components. So, one must make ad-hoc choices that will work for the example, but which may run into trouble more generally. F90 supports facilities for allocating items of a size determined at run time, however this has to be done before the item is put to use, such as in a READ statement. But the input data are not to be parsed by special code, so reading an input line into a working area to determine the necessary sizes, allocating storage areas of suitable size, then transferring data from the scratchpad to the freshly-created recipients is disallowed here.
 
The source is unstructured by subroutines and the like, but uses the facilities of F90 whereby a compound data aggregate can be defined and used with a systematic nomenclature for the parts. Thus, array EMPLOYEE has components with suitable names. Previously, one would define separate simple arrays of the appropriate type, using a similar naming system in self-defence: perhaps ENAME, EID, ESAL, EDEPT or similar.
 
If the requirement had been to report the highest-ranked salary, the deed could have been done via the F90 function MAXLOC, which locates the array index of the (first-encountered?) maximum value of an array of values; further, it allows an additional MASK parameter which could be used to select only those entries having a particular "department" code. However, aside from the multiple scans that would be required for multiple departmental codes, there is the requirement for the top N salaries, and anyway, there would still be the need to identify the different departmental codes once only. Accordingly, the way ahead is the classic: sort the data and then work through the sorted sequence. Since standard Fortran provides no "sort" function in its standard libraries nor does it offer a pre-processor that might include a sort generator and the like to generate the specific code needed for a particular data aggregate and sort key specification, one must engage in some repetition. For this purpose, the "comb" sort is convenient with its code requiring just one comparison and one "swap" action, so in-line code is not troublesome. On the other hand, when dealing with compound sort keys, a three-way IF statement (or equivalent) is necessary whereby the first field of the sort key is compared with appropriate action for the < and > cases, but for the = case the comparison moves on to the second key. ''Three'' different consequences from the one comparison. Despite the deprecations of the modernisers this is still possible for numerical comparisons, but alas, character comparisons are not allowed in an arithmetic-IF statement - though one could struggle with ICHAR action. Thus, two comparisons are made where only one should suffice.
 
Finally, standard Fortran does not offer a means of receiving parameters as might be supplied when a code file is activated in a command-line environment. There may be special library routines with names such as GETARG, but they're not standard. So, a READ statement is employed. Or, one could rewrite this routine as a subroutine having one parameter so as to approximate the form of the specification more closely. Extra statements... <syntaxhighlight lang="fortran"> PROGRAM TOP RANK !Just do it.
CHARACTER*28 HEADING(4) !The first line is a header.
TYPE GRIST !But this is the stuff.
CHARACTER*28 NAME !Arbitrary sizes.
CHARACTER*6 ID !Possibly imperfect.
REAL*8 SALARY !Single precision is a bit thin.
CHARACTER*6 DEPARTMENT !Not a number.
END TYPE GRIST !So much for the aggregate.
INTEGER HORDE !Some parameterisation.
PARAMETER (HORDE = 66) !This should suffice.
TYPE(GRIST) EMPLOYEE(HORDE),HIC !An extra for the sort.
LOGICAL CURSE !Possible early completion.
INTEGER I,N,H !Steppers.
INTEGER II,R !Needed for the results.
INTEGER KBD,MSG,IN !I/O unit numbers.
 
KBD = 5 !Standard input.
MSG = 6 !Standard output.
IN = 10 !Suitable for an input file.
WRITE (MSG,1)
1 FORMAT ("Reads a set of employee information from TopRank.csv"/
1"Then for each department code, shows the N highest paid.")
OPEN (IN,NAME = "TopRank.csv",FORM = "FORMATTED")
READ (IN,*) HEADING !Column headings: the "Salary" heading is not numeric.
Chug through the input.
N = 0 !None so far.
10 READ (IN,*,END = 20) EMPLOYEE(N + 1) !Get the next record.
N = N + 1 !We did. Count it in.
IF (N.GT.HORDE) STOP "Too many employee records!" !Ah, suspicion.
GO TO 10 !Perhaps there will be another.
Collate the collection.
20 CLOSE(IN) !Finished with the input.
Crank up a comb sort, which requires only one comparison statement. Especially good for compound fields.
H = N - 1 !Last - first, and not + 1.
21 H = MAX(1,H*10/13) !The special feature.
CURSE = .FALSE. !So far, so good.
DO I = N - H,1,-1 !If H = 1, this is a Bubblesort.
IF (EMPLOYEE(I).DEPARTMENT.LT.EMPLOYEE(I + H).DEPARTMENT) CYCLE !In order by department.
IF (EMPLOYEE(I).DEPARTMENT.EQ.EMPLOYEE(I + H).DEPARTMENT !Or, Equal department,
* .AND. EMPLOYEE(I).SALARY.GT.EMPLOYEE(I + H).SALARY) CYCLE !And in decreasing order by salary.
CURSE = .TRUE. !No escape. the elements are in the wrong order.
HIC = EMPLOYEE(I) !So a SWAP statement would be good. But alas.
EMPLOYEE(I) = EMPLOYEE(I + H) !For large data aggregates, an indexed sort would be good.
EMPLOYEE(I + H) = HIC !But, just slog away.
END DO !Consider the next pairing.
IF (CURSE .OR. H.GT.1) GO TO 21 !Work remains?
 
Cast forth results.
30 WRITE (6,31) N !Announce, and solicit a parameter.
31 FORMAT (I0," employees. How many per dept? ",$) !The $ sez "don't start a new line."
READ (KBD,*) R !The parameter.
IF (R.LE.0) STOP !bah.
WRITE (MSG,32) "Rank",HEADING
32 FORMAT (/,A6,1X,A28,2X,A12,1X,A10,A) !Compare to FORMAT 33.
HIC.DEPARTMENT = "...Not this" !Different from all departmental codes.
DO I = 1,N !Scan the sorted data.
IF (HIC.DEPARTMENT.EQ.EMPLOYEE(I).DEPARTMENT) THEN !Another?
II = II + 1 !Same department, so count another adherent.
ELSE !But with a change of department code,
II = 1 !Start a fresh count.
HIC.DEPARTMENT = EMPLOYEE(I).DEPARTMENT !And remember the new code.
WRITE (MSG,*) "For ",HIC.DEPARTMENT !Announce the new department's code.
END IF !So much for grouping.
IF (II.LE.R) WRITE (MSG,33) II,EMPLOYEE(I) !Still within the desired rank?
33 FORMAT (I6,1X,A28,1X,A12,F11.2,1X,A) !Some layout. Includes the repeated departmental code name.
END DO !On to the next.
 
END !That was straightforward.
</syntaxhighlight>
 
As ever, some tricky sizing of fields in the FORMAT statements, and a collection of ad-hoc constants. Cross-linking the integers via PARAMETER statements and the like would add a whole lot of blather, so if field sizes are changed, a fair amount of fiddling would follow. A more accomplished data processing routine would include this.
 
Output:
<pre>
Reads a set of employee information from TopRank.csv
Then for each department code, shows the N highest paid.
13 employees. How many per dept? 3
 
Rank Employee Name Employee ID Salary Department
For D050
1 John Rappl E21437 47000.00 D050
2 Nathan Adams E41298 21900.00 D050
For D101
1 George Woltman E00127 53500.00 D101
2 David McClellan E04242 41500.00 D101
3 Tyler Bennett E10297 32000.00 D101
For D190
1 Kim Arlich E10001 57000.00 D190
2 Timothy Grove E16398 29900.00 D190
For D202
1 Rich Holcomb E01234 49500.00 D202
2 Claire Buckman E39876 27800.00 D202
3 David Motsinger E27002 19250.00 D202
</pre>
 
=={{header|FreeBASIC}}==
<syntaxhighlight lang="freebasic">#define N 3 'show the top three employees of each rank
 
'here is all the data to be read in
 
data "Tyler Bennett","E10297",32000,"D101"
data "John Rappl","E21437",47000,"D050"
data "George Woltman","E00127",53500,"D101"
data "Adam Smith","E63535",18000,"D202"
data "Claire Buckman","E39876",27800,"D202"
data "David McClellan","E04242",41500,"D101"
data "Rich Holcomb","E01234",49500,"D202"
data "Nathan Adams","E41298",21900,"D050"
data "Richard Potter","E43128",15900,"D101"
data "David Motsinger","E27002",19250,"D202"
data "Tim Sampair","E03033",27000,"D101"
data "Kim Arlich","E10001",57000,"D190"
data "Timothy Grove","E16398",29900,"D190"
 
type employee
'define a data type for employees
nm as string*32 'name
en as string*6 'employee number
sl as uinteger 'salary
dp as string*4 'department
fl as boolean 'a flag
end type
 
dim as employee emp(1 to 13)
dim as uinteger e, d, x, ce, cs
dim as string*4 dept(1 to 4) = {"D050", "D101", "D190", "D202"}
 
for e = 1 to 13
'put all the employee data into an array
read emp(e).nm
read emp(e).en
read emp(e).sl
read emp(e).dp
emp(e).fl = false
next e
 
for d = 1 to 4 'look at each department
print "Department ";dept(d);":"
for x = 1 to N 'top N employees
cs = 0
ce = 0
for e = 1 to 13 'look through employees
if emp(e).dp = dept(d) andalso emp(e).fl = false andalso emp(e).sl > cs then
emp(ce).fl = false 'unflag the previous champion so they can be found on the next pass
ce = e
cs = emp(e).sl
emp(e).fl = true 'flag this employee so that on the next pass we can get the next richest
endif
next e
if ce>0 then print emp(ce).nm;" ";emp(ce).en;" ";emp(ce).sl;" ";emp(ce).dp
next x
print
next d</syntaxhighlight>
 
{{out}}<pre>
Department D050:
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
 
Department D101:
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Tyler Bennett E10297 32000 D101
 
Department D190:
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
 
Department D202:
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
David Motsinger E27002 19250 D202
</pre>
 
=={{header|FunL}}==
<syntaxhighlight lang="funl">data Employee( name, id, salary, dept )
 
employees = [
Employee( 'Tyler Bennett', 'E10297', 32000, 'D101' ),
Employee( 'John Rappl', 'E21437', 47000, 'D050' ),
Employee( 'George Woltman', 'E00127', 53500, 'D101' ),
Employee( 'Adam Smith', 'E63535', 18000, 'D202' ),
Employee( 'Claire Buckman', 'E39876', 27800, 'D202' ),
Employee( 'David McClellan', 'E04242', 41500, 'D101' ),
Employee( 'Rich Holcomb', 'E01234', 49500, 'D202' ),
Employee( 'Nathan Adams', 'E41298', 21900, 'D050' ),
Employee( 'Richard Potter', 'E43128', 15900, 'D101' ),
Employee( 'David Motsinger', 'E27002', 19250, 'D202' ),
Employee( 'Tim Sampair', 'E03033', 27000, 'D101' ),
Employee( 'Kim Arlich', 'E10001', 57000, 'D190' ),
Employee( 'Timothy Grove', 'E16398', 29900, 'D190' )
]
 
N = 2
 
for (dept, empl) <- employees.groupBy( e -> e.dept ).>toList().sortWith( (<) )
println( dept )
for e <- empl.sortWith( \a, b -> a.salary > b.salary ).take( N )
printf( " %-16s %6s %7d\n", e.name, e.id, e.salary )
println()</syntaxhighlight>
{{out}}
 
<pre>
D050
John Rappl E21437 47000
Nathan Adams E41298 21900
 
D101
George Woltman E00127 53500
David McClellan E04242 41500
 
D190
Kim Arlich E10001 57000
Timothy Grove E16398 29900
 
D202
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
</pre>
 
=={{header|Go}}==
<syntaxhighlight lang="go">package main
Handling the possibility of ties:
<lang go>package main
 
import (
Line 835 ⟶ 2,360:
 
// language-native data description
type employeeEmployee struct {
nameName, idID string
salarySalary int
deptDept string
}
 
type EmployeeList []*Employee
var data = []employee{
 
var data = EmployeeList{
{"Tyler Bennett", "E10297", 32000, "D101"},
{"John Rappl", "E21437", 47000, "D050"},
Line 855 ⟶ 2,382:
{"Kim Arlich", "E10001", 57000, "D190"},
{"Timothy Grove", "E16398", 29900, "D190"},
// Extra data to demonstrate ties
{"Tie A", "E16399", 29900, "D190"},
{"Tie B", "E16400", 29900, "D190"},
{"No Tie", "E16401", 29899, "D190"},
}
 
// We only need one type of ordering/grouping for this task so we could directly
// n provided as first parameter of function topN
// implement sort.Interface on EmployeeList (or a byDeptSalary alias type) with
func main() {
// the appropriate Less method.
topN(3, data).print()
//
// Instead, we'll add a bit here that makes it easier to use arbitrary orderings.
// This is like the "SortKeys" Planet sorting example in the sort package
// documentation, see https://golang.org/pkg/sort
 
type By func(e1, e2 *Employee) bool
type employeeSorter struct {
list EmployeeList
by func(e1, e2 *Employee) bool
}
 
func (by By) Sort(list EmployeeList) { sort.Sort(&employeeSorter{list, by}) }
// return type of function topN
func (s *employeeSorter) Len() int { return len(s.list) }
type result struct {
func (s *employeeSorter) Swap(i, j int) { s.list[i], s.list[j] = s.list[j], s.list[i] }
n int
func (s *employeeSorter) Less(i, j int) bool { return s.by(s.list[i], s.list[j]) }
dlist [][][]int // dimensions are department,salary,index into data
}
 
// For this specific task we could just write the data to an io.Writer
func (r result) print() {
// but in general it's better to return the data in a usable form (for
fmt.Println("Top", r.n, "salaries per department")
// example, perhaps other code want's to do something like compare the
printEs := func(es []int) {
// averages of the top N by department).
for _, ei := range es {
//
fmt.Printf(" %s %16s %7d\n",
// So we go through the extra effort of returning an []EmployeeList, a
data[ei].id, data[ei].name, data[ei].salary)
// list of employee lists, one per deparment. The lists are trimmed to
}
// to the top 'n', which can be larger than n if there are ties for the
// nth salary (callers that don't care about ties could just trim more.)
func (el EmployeeList) TopSalariesByDept(n int) []EmployeeList {
if n <= 0 || len(el) == 0 {
return nil
}
for _, slistdeptSalary := rangefunc(e1, e2 *Employee) r.dlistbool {
fmtif e1.Println(data[slist[0][0]]Dept != e2.dept)Dept {
nRemaining := r return e1.Dept < e2.nDept
sLast := len(slist) - 1
for si := 0; si < sLast; si++ {
printEs(slist[si])
nRemaining -= len(slist[si])
}
if len(slist[sLast])e1.Salary >!= nRemaininge2.Salary {
fmtreturn e1.Println("Salary > * * * * * * * tie * * * * * * *")e2.Salary
}
// Always have some unique field as the last one in a sort list
printEs(slist[sLast])
return e1.ID < e2.ID
}
}
 
// We could just sort the data in place for this task. But
func topN(n int, data []employee) *result {
// perhaps messing with the order is undesirable or there is
// summarize: construct map with unique deptartment names,
// other concurrent access. So we'll make a copy and sort that.
// unique salaries under each of those,
// andIt's thenjust apointers listso ofthe employeesamount underto eachcopy ofis those,relatively small.
sorted := make(EmployeeList, len(el))
// with employees identified by index into data.
copy(sorted, el)
salsum := make(map[string]map[int][]int)
By(deptSalary).Sort(sorted)
for i, e := range data {
 
dsum, ok := salsum[e.dept]
perDept := if !ok []EmployeeList{}
var lastDept string
dsum = make(map[int][]int)
var lastSalary int
salsum[e.dept] = dsum
for _, e := range sorted {
if e.Dept != lastDept || len(perDept) == 0 {
lastDept = e.Dept
perDept = append(perDept, EmployeeList{e})
} else {
i := len(perDept) - 1
if len(perDept[i]) >= n && e.Salary != lastSalary {
continue
}
perDept[i] = append(perDept[i], e)
lastSalary = e.Salary
}
dsum[e.salary] = append(dsum[e.salary], i)
}
return perDept
}
 
func main() {
// begin constructing result array.
const n = 3
// top level is list of departments.
top := data.TopSalariesByDept(n)
dlist := make([][][]int, len(salsum))
if len(top) == 0 {
 
fmt.Println("Nothing to show.")
// sort departments for readability
return
deptSort := make([]string, len(salsum))
i := 0
for dept := range salsum {
deptSort[i] = dept
i++
}
fmt.Printf("Top %d salaries per department\n", n)
sort.Strings(deptSort)
for _, list := range top {
 
fmt.Println(list[0].Dept)
for di, dept := range deptSort {
dsumfor _, e := salsum[dept]range list {
fmt.Printf(" %s %16s %7d\n", e.ID, e.Name, e.Salary)
// sort salaries for selection (and readability)
salSort := make([]int, len(dsum))
i := 0
for s := range dsum {
salSort[i] = s
i++
}
sort.Ints(salSort)
nRemaining := n
// construct level 2, unique salaries
var slist [][]int
// iterate from end so as to select highest
for si := len(salSort) - 1; si >= 0; si-- {
ssum := dsum[salSort[si]]
// construct level 3, indexes into data
elist := make([]int, len(ssum))
slist = append(slist, elist)
copy(elist, ssum)
nRemaining -= len(ssum)
if nRemaining <= 0 {
break
}
}
dlist[di] = slist
}
}</syntaxhighlight>
return &result{n, dlist}
{{out|Output (with additional data demonstrating ties)}}
}</lang>
<pre style="height:30ex;overflow:scroll">
Output with specified data set:
<pre>
Top 3 salaries per department
D050
Line 962 ⟶ 2,486:
E10001 Kim Arlich 57000
E16398 Timothy Grove 29900
E16399 Tie A 29900
D202
E01234E16400 Rich Holcomb 49500 Tie B 29900
E39876 Claire Buckman 27800
E27002 David Motsinger 19250
</pre>
Output with additional data demonstrating ties:
<pre>
Top 3 salaries per department
D050
E21437 John Rappl 47000
E41298 Nathan Adams 21900
D101
E00127 George Woltman 53500
E00128 George Waltman 53500
E04242 David McClellan 41500
D190
E10001 Kim Arlich 57000
* * * * * * * tie * * * * * * *
E16398 Timothy Grove 29900
E16399 Timothy Grave 29900
E16400 Timothy Grive 29900
D202
E01234 Rich Holcomb 49500
Line 990 ⟶ 2,495:
 
=={{header|Groovy}}==
<langsyntaxhighlight lang="groovy">def displayRank(employees, number) {
employees.groupBy { it.Department }.sort().each { department, staff ->
println "Department $department"
Line 1,017 ⟶ 2,522:
[Name: 'Timothy Grove', ID: 'E16398', Salary: 29900, Department: 'D190']
]
displayRank(employees, 3)</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department D050
Output:
<pre>Department D050
Name ID Salary
John Rappl E21437 47000
Line 1,039 ⟶ 2,543:
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
David Motsinger E27002 19250</pre>
</pre>
 
=={{header|Haskell}}==
====Data.List====
{{Works with|GHC|7.10.3}}
<syntaxhighlight lang="haskell">import Data.List (sortBy, groupBy)
 
import Text.Printf (printf)
<lang haskell>import Data.List
import Control.Monad
import Control.Arrow
import Text.Printf
import Data.Ord
import Data.Function
 
import Data.Ord (comparing)
groupingOn = ((==) `on`)
comparingDwn = flip . comparing
 
import Data.Function (on)
 
type ID = Int
 
type DEP = String
 
type NAME = String
 
type SALARY = Double
 
data Employee = Employee {nr :: ID, dep :: DEP, name :: NAME, sal :: SALARY}
data Employee = Employee
{ nr :: ID
, dep :: DEP
, name :: NAME
, sal :: SALARY
}
 
employees :: [Employee]
employees = map (\(i,d,n,s) -> Employee i d n s)
fmap
[(1001,"AB","Janssen A.H.",41000), (101,"KA","'t Woud B.",45000),
(\(i, d, n, s) -> Employee i d n s)
(1013,"AB","de Bont C.A.",65000), (1101,"CC","Modaal A.M.J.",30000),
[ (12031001, "AB", "AndersJanssen A.H.",50000), (100,"KA","Ezelbips P.J.",5200041000),
, (1102101, "CCKA","Zagt A.",33000),'t Woud (1103,"CCB.","Ternood T.R.",2100045000),
, (11041013, "CCAB","Lageln M.",23000),de Bont (1105,"CC","Amperwat C.A.",19000 65000),
, (11061101, "CC", "BoonModaal TA.M.J.",25000), (1107,"CC","Beloop L.O.",3100030000),
, (10091203, "CDAB","Janszoon A.",38665), (1026,"CD","JanszenAnders H.P.",41000 50000),
, (1011100,"CC","de Goeij J.",39000), (106,"KA", "PragtweikEzelbips P.J.M.V.",42300 52000),
, (1111102, "KACC", "BakeuroZagt SA.",31000), (105,"KA","Clubdrager C.",3980033000),
, (1041103, "KACC", "KarendijkTernood FT.",23000), (107,"KA","Centjes R.M.",34000 21000),
, (1191104, "KACC","Tegenstroom H.L.",39000),Lageln (1111,"CD","Telmans R.M.",55500 23000),
, (10931105,"AB","de Slegte S.",46987), (1199,"CC", "UitlaatAmperwat G.A.S.",44500 19000)
, (1106, "CC", "Boon T.J.", ]25000)
, (1107, "CC", "Beloop L.O.", 31000)
, (1009, "CD", "Janszoon A.", 38665)
, (1026, "CD", "Janszen H.P.", 41000)
, (1011, "CC", "de Goeij J.", 39000)
, (106, "KA", "Pragtweik J.M.V.", 42300)
, (111, "KA", "Bakeuro S.", 31000)
, (105, "KA", "Clubdrager C.", 39800)
, (104, "KA", "Karendijk F.", 23000)
, (107, "KA", "Centjes R.M.", 34000)
, (119, "KA", "Tegenstroom H.L.", 39000)
, (1111, "CD", "Telmans R.M.", 55500)
, (1093, "AB", "de Slegte S.", 46987)
, (1199, "CC", "Uitlaat G.A.S.", 44500)
]
 
dorankfirstN :: Int ->
-> (Employee -> DEP) ->
-> (Employee -> SALARY) ->
[Employee]-> [[Employee]]
-> [[Employee]]
dorank n o1 o2 = map (take n. sortBy (comparingDwn o2))
firstN n o1 o2 x =
. groupBy (groupingOn o1) . sortBy (comparing o1)
fmap
(take n . sortBy (comparingDown o2))
(groupBy (groupingOn o1) (sortBy (comparing o1) x))
 
groupingOn
toprank :: IO ()
:: Eq a1
toprank = do
=> printf(a "%-16s> %3sa1) %10s\n"-> "NAME"a "DEP"-> "TIP"a -> Bool
groupingOn = ((==) `on`)
putStrLn $ replicate 31 '='
 
mapM_ (mapM_ (ap (ap (printf "%-16s %3s %10.2g\n" . name) dep) sal)) $ dorank 3 dep sal employees</lang>
comparingDown
Output: top 3 per department
:: Ord a
<pre>
=> (b -> a) -> b -> b -> Ordering
*Main> toprank
comparingDown = flip . comparing
NAME DEP TIP
 
main :: IO ()
main = do
printf "%-16s %3s %10s\n" "NAME" "DEP" "TIP"
putStrLn $ replicate 31 '='
mapM_ (traverse ((printf "%-16s %3s %10.2g\n" . name) <*> dep <*> sal)) $
firstN 3 dep sal employees</syntaxhighlight>
{{out}}
<pre>NAME DEP TIP
===============================
de Bont C.A. AB 65000.00
Line 1,105 ⟶ 2,641:
Ezelbips P.J. KA 52000.00
't Woud B. KA 45000.00
Pragtweik J.M.V. KA 42300.00</pre>
 
</pre>
====Data.Map====
{{Works with|GHC|7.10.3}}
 
Alternatively, if we store the data in key-value maps, rather than in a cons list, we can use Map.lookup and Map.filter, Map.keys and Map.elems, to pull out the data and shape reports fairly flexibly.
 
<syntaxhighlight lang="haskell">import Data.List (intercalate, nub, sort, sortBy, transpose)
import qualified Data.Map as M
import Data.Maybe (fromJust)
import Data.Ord (comparing)
 
main :: IO ()
main =
(putStrLn . unlines) $
table
" "
( (reportLines <*> highSalaryKeys 3)
=<< (sort . nub) (M.elems mapDepts)
)
 
reportLines :: String -> [(Int, Int)] -> [[String]]
reportLines dept =
fmap
( \(k, n) ->
[ fromJust $ M.lookup k mapNames,
dept,
show n
]
)
 
highSalaryKeys :: Int -> String -> [(Int, Int)]
highSalaryKeys n dept =
take n $
sortBy ((flip . comparing) snd) $
(,) <*> (fromJust . flip M.lookup mapSalaries)
<$> M.keys (M.filter (== dept) mapDepts)
 
mapNames, mapDepts :: M.Map Int String
[mapNames, mapDepts] =
(readPairs <$> [nameKV, deptKV]) <*> [xs]
 
mapSalaries :: M.Map Int Int
mapSalaries = readPairs salaryKV xs
 
xs :: [(Int, String, String, Int)]
xs =
[ (1001, "AB", "Janssen A.H.", 41000),
(101, "KA", "'t Woud B.", 45000),
(1013, "AB", "de Bont C.A.", 65000),
(1101, "CC", "Modaal A.M.J.", 30000),
(1203, "AB", "Anders H.", 50000),
(100, "KA", "Ezelbips P.J.", 52000),
(1102, "CC", "Zagt A.", 33000),
(1103, "CC", "Ternood T.R.", 21000),
(1104, "CC", "Lageln M.", 23000),
(1105, "CC", "Amperwat A.", 19000),
(1106, "CC", "Boon T.J.", 25000),
(1107, "CC", "Beloop L.O.", 31000),
(1009, "CD", "Janszoon A.", 38665),
(1026, "CD", "Janszen H.P.", 41000),
(1011, "CC", "de Goeij J.", 39000),
(106, "KA", "Pragtweik J.M.V.", 42300),
(111, "KA", "Bakeuro S.", 31000),
(105, "KA", "Clubdrager C.", 39800),
(104, "KA", "Karendijk F.", 23000),
(107, "KA", "Centjes R.M.", 34000),
(119, "KA", "Tegenstroom H.L.", 39000),
(1111, "CD", "Telmans R.M.", 55500),
(1093, "AB", "de Slegte S.", 46987),
(1199, "CC", "Uitlaat G.A.S.", 44500)
]
 
readPairs ::
Ord k =>
(a1 -> (k, a)) ->
[a1] ->
M.Map k a
readPairs f xs = M.fromList (f <$> xs)
 
nameKV,
deptKV ::
(Int, String, String, Int) -> (Int, String)
nameKV (k, _, name, _) = (k, name)
deptKV (k, dept, _, _) = (k, dept)
 
salaryKV :: (Int, String, String, Int) -> (Int, Int)
salaryKV (k, _, _, salary) = (k, salary)
 
table :: String -> [[String]] -> [String]
table delim rows =
let justifyLeft c n s = take n (s <> replicate n c)
justifyRight c n s =
drop
(length s)
(replicate n c <> s)
in intercalate delim
<$> transpose
( ( fmap
=<< justifyLeft ' '
. maximum
. fmap length
)
<$> transpose rows
)</syntaxhighlight>
{{Out}}
<pre>de Bont C.A. AB 65000
Anders H. AB 50000
de Slegte S. AB 46987
Uitlaat G.A.S. CC 44500
de Goeij J. CC 39000
Zagt A. CC 33000
Telmans R.M. CD 55500
Janszen H.P. CD 41000
Janszoon A. CD 38665
Ezelbips P.J. KA 52000
't Woud B. KA 45000
Pragtweik J.M.V. KA 42300</pre>
 
=={{header|HicEst}}==
<langsyntaxhighlight HicEstlang="hicest">CHARACTER source="Test.txt", outP='Top_rank.txt', fmt='A20,A8,i6,2x,A10'
CHARACTER name*20, employee_ID*10, department*10, temp*10
REAL :: idx(1), N_top_salaries=3
Line 1,138 ⟶ 2,790:
ENDDO
 
END</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
 
Line 1,156 ⟶ 2,808:
 
=={{header|Icon}} and {{header|Unicon}}==
<syntaxhighlight lang="icon">record Employee(name,id,salary,dept)
 
<lang icon>
record Employee(name,id,salary,dept)
 
procedure getEmployees ()
Line 1,201 ⟶ 2,851:
every show_employee (!employeesInGroup[1:(1+min(N,*employeesInGroup))])
}
end</syntaxhighlight>{{out}}
end
<pre style="height:30ex;overflow:scroll">
</lang>
 
Output:
<pre>
$ ./top-rank-by-group 2
== Top 2 in group D101
Line 1,236 ⟶ 2,883:
== Top 4 in group D190
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190 </pre>
 
</pre>
=={{header|IS-BASIC}}==
<syntaxhighlight lang="is-basic">100 PROGRAM "Employee.bas"(N)
110 LET NR=100:LET X=0
120 STRING NAME$(1 TO NR)*20,ID$(1 TO NR)*6,DEPT$(1 TO NR)*4
130 NUMERIC SALARY(1 TO NR)
140 DO UNTIL N>0 AND N<=NR
150 INPUT PROMPT "Enter the number of ranks: ":N$
160 LET N=VAL(N$)
170 LOOP
180 CALL READDATA
190 CALL SORT
200 CALL LST
210 END
220 DEF READDATA
230 LET EOF=0
240 OPEN #1:"Employee.dat"
250 WHEN EXCEPTION USE IOERROR
260 DO
270 LET X=X+1
280 INPUT #1:NAME$(X),ID$(X),SALARY(X),DEPT$(X)
290 LOOP UNTIL EOF OR X=NR
300 END WHEN
310 HANDLER IOERROR
320 IF EXTYPE<>8001 THEN PRINT EXSTRING$(EXTYPE)
330 CLOSE #1
340 LET X=X-1:LET EOF=-1
350 IF X=0 THEN PRINT "No data.":STOP
360 CONTINUE
370 END HANDLER
380 END DEF
390 DEF SORT
400 LET GAP=X:LET SW=1
410 DO WHILE GAP>1 OR SW
420 LET GAP=MAX(INT(GAP/1.3),1):LET SW=0
430 FOR I=1 TO X-GAP
440 IF DEPT$(I)>DEPT$(I+GAP) OR DEPT$(I)=DEPT$(I+GAP) AND SALARY(I)<SALARY(I+GAP) THEN
450 LET T$=NAME$(I):LET NAME$(I)=NAME$(I+GAP):LET NAME$(I+GAP)=T$
460 LET T$=DEPT$(I):LET DEPT$(I)=DEPT$(I+GAP):LET DEPT$(I+GAP)=T$
470 LET T$=ID$(I):LET ID$(I)=ID$(I+GAP):LET ID$(I+GAP)=T$
480 LET T=SALARY(I):LET SALARY(I)=SALARY(I+GAP):LET SALARY(I+GAP)=T
490 LET SW=1
500 END IF
510 NEXT
520 LOOP
530 END DEF
540 DEF LST
550 LET J=1:LET PREV$=""
560 FOR I=1 TO X
570 IF DEPT$(I)<>PREV$ THEN PRINT "Department ";DEPT$(I):LET PREV$=DEPT$(I):LET J=1
580 IF J<=N THEN PRINT " ";NAME$(I);TAB(23);ID$(I),SALARY(I):LET J=J+1
590 NEXT
600 END DEF</syntaxhighlight>
 
=={{header|J}}==
J has a rich set of primitive functions, which combine the power of an imperative language with the expressiveness of a declarative, SQL-like language:
 
<langsyntaxhighlight lang="j">NB. Dynamically generate convenience functions
('`',,;:^:_1: N=:{.Employees) =:, (_&{"1)`'' ([^:(_ -: ])L:0)"0 _~ i.# E =: {: Employees
NB. Show top six ranked employees in each dept
N , (<@:>"1@:|:@:((6 <. #) {. ] \: SALARY)/.~ DEPT) |: <"1&> E</langsyntaxhighlight>
<pre style="height:30ex;overflow:scroll">
+-----+-----+-----------------+------+
|ID |DEPT |NAME |SALARY|
Line 1,272 ⟶ 2,972:
|1026 |CD |Janszen H.P. |41000 |
|1009 |CD |Janszoon A. |38665 |
+-----+-----+-----------------+------+</pre>
 
using the data set:
 
using the data set:<pre style="height:30ex;overflow:scroll">
Employees=: (<;.1~(1 1{.~#);+./@:(;:E.S:0])@:{.)];._2 noun define
ID DEPT NAME SALARY
Line 1,302 ⟶ 3,001:
1093 AB de Slegte S. 46987
1199 CC Uitlaat G.A.S. 44500
)</pre>
 
Named as a function where the (maximum) number of employees in each department is a parameter:
 
<langsyntaxhighlight lang="j">reportTopSalaries=: 3 :'N , (<@:>"1@:|:@:((y <. #) {. ] \: SALARY)/.~ DEPT) |: <"1&> E'</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">
Example of use:
reportTopSalaries 2
+-----+-----+-----------------+------+
Line 1,323 ⟶ 3,022:
|1111 |CD |Telmans R.M. |55500 |
|1026 |CD |Janszen H.P. |41000 |
+-----+-----+-----------------+------+</pre>
 
=={{header|Java}}==
{{works with|Java|8}}
<syntaxhighlight lang="java">import java.io.File;
import java.util.*;
 
public class TopRankPerGroup {
 
private static class Employee {
final String name;
final String id;
final String department;
final int salary;
 
Employee(String[] rec) {
name = rec[0];
id = rec[1];
salary = Integer.parseInt(rec[2]);
department = rec[3];
}
 
@Override
public String toString() {
return String.format("%s %s %d %s", id, name, salary, department);
}
}
 
public static void main(String[] args) throws Exception {
int N = args.length > 0 ? Integer.parseInt(args[0]) : 3;
 
Map<String, List<Employee>> records = new TreeMap<>();
try (Scanner sc = new Scanner(new File("data.txt"))) {
while (sc.hasNextLine()) {
String[] rec = sc.nextLine().trim().split(", ");
 
List<Employee> lst = records.get(rec[3]);
if (lst == null) {
lst = new ArrayList<>();
records.put(rec[3], lst);
}
lst.add(new Employee(rec));
}
}
 
records.forEach((key, val) -> {
System.out.printf("%nDepartment %s%n", key);
val.stream()
.sorted((a, b) -> Integer.compare(b.salary, a.salary))
.limit(N).forEach(System.out::println);
});
}
}</syntaxhighlight>
 
<pre style="height:30ex;overflow:scroll">Department D050
E21437 John Rappl 47000 D050
E41298 Nathan Adams 21900 D050
 
Department D101
E00127 George Woltman 53500 D101
E04242 David McClellan 41500 D101
E10297 Tyler Bennett 32000 D101
 
Department D190
E10001 Kim Arlich 57000 D190
E16398 Timothy Grove 29900 D190
 
Department D202
E01234 Rich Holcomb 49500 D202
E39876 Claire Buckman 27800 D202
E27002 David Motsinger 19250 D202</pre>
 
=={{header|JavaScript}}==
 
<lang javascript>var data = [
===Iterative Solution===
<syntaxhighlight lang="javascript">var data = [
{name: "Tyler Bennett", id: "E10297", salary: 32000, dept: "D101"},
{name: "John Rappl", id: "E21437", salary: 47000, dept: "D050"},
Line 1,354 ⟶ 3,125:
}
 
// group by dept, and sort by balarysalary
function group_by_dept(data) {
var by_dept = {};
Line 1,383 ⟶ 3,154:
}
 
top_rank(3);</lang>
</syntaxhighlight>
 
{{out}}
outputs:
<pre style="height:30ex;overflow:scroll">D101
<pre>D101
George Woltman, id=E00127, salary=53500
David McClellan, id=E04242, salary=41500
Line 1,399 ⟶ 3,170:
Claire Buckman, id=E39876, salary=27800
David Motsinger, id=E27002, salary=19250
 
D190
Kim Arlich, id=E10001, salary=57000
Timothy Grove, id=E16398, salary=29900</pre>
 
 
 
===Map and reduce===
====ES5====
<syntaxhighlight lang="javascript">var collectDept = function (arrOfObj) {
var collect = arrOfObj.reduce(function (rtnObj, obj) {
if (rtnObj[obj.dept] === undefined) {
rtnObj[obj.dept] = [];
}
rtnObj[obj.dept].push(obj);
return rtnObj;
}, {});
 
return Object.keys(collect).map(function (key) {
return collect[key];
});
};
 
var sortSalary = function (arrOfSalaryArrs) {
return arrOfSalaryArrs.map(function (item) {
return item.sort(function (a, b) {
if (a.salary > b.salary) { return -1; }
if (a.salary < b.salary) { return 1; }
return 0;
});
});
};
 
var getNTopSalariesByDept = function (n, data) {
if (n < 0) { return; }
 
return sortSalary(collectDept(data)).map(function (list) {
return list.slice(0,n);
});
};</syntaxhighlight>
====ES6====
By composition of generic functions:
<syntaxhighlight lang="javascript">(() => {
'use strict';
 
// topNSalariesPerDept :: Int -> [[String]] -> [String]
const topNSalariesPerDept = (n, records) =>
foldl(
(a, k, i) => (a[toLower(k)] = x => x[i], a),
this,
head(records)
) && map(intercalate(','),
concatMap(take(n),
reverse(
groupBy(
on(same, department),
sortBy(
flip(
mappendComparing([
department,
salary
])
),
tail(records)
)
)
)
)
);
 
// GENERIC FUNCTIONS -----------------------------------------------------
 
// comparing :: (a -> b) -> (a -> a -> Ordering)
const comparing = f =>
(x, y) => {
const
a = f(x),
b = f(y);
return a < b ? -1 : (a > b ? 1 : 0);
};
 
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = (f, xs) =>
xs.length > 0 ? (() => {
const unit = typeof xs[0] === 'string' ? '' : [];
return unit.concat.apply(unit, xs.map(f));
})() : [];
 
// curry :: Function -> Function
const curry = (f, ...args) => {
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
function () {
return go(xs.concat(Array.from(arguments)));
};
return go([].slice.call(args, 1));
};
 
// flip :: (a -> b -> c) -> b -> a -> c
const flip = f => (a, b) => f.apply(null, [b, a]);
 
// foldl :: (b -> a -> b) -> b -> [a] -> b
const foldl = (f, a, xs) => xs.reduce(f, a);
 
// groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
const groupBy = (f, xs) => {
const dct = xs.slice(1)
.reduce((a, x) => {
const
h = a.active.length > 0 ? a.active[0] : undefined,
blnGroup = h !== undefined && f(h, x);
return {
active: blnGroup ? a.active.concat([x]) : [x],
sofar: blnGroup ? a.sofar : a.sofar.concat([a.active])
};
}, {
active: xs.length > 0 ? [xs[0]] : [],
sofar: []
});
return dct.sofar.concat(dct.active.length > 0 ? [dct.active] : []);
};
 
// head :: [a] -> a
const head = xs => xs.length ? xs[0] : undefined;
 
// intercalate :: String -> [a] -> String
const intercalate = curry((s, xs) => xs.join(s));
 
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
 
// mappendComparing :: [(a -> b)] -> (a -> a -> Ordering)
const mappendComparing = fs => (x, y) =>
fs.reduce((ord, f) => (ord !== 0) ? (
ord
) : (() => {
const
a = f(x),
b = f(y);
return a < b ? -1 : a > b ? 1 : 0
})(), 0);
 
// on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
const on = (f, g) => (a, b) => f(g(a), g(b));
 
// reverse :: [a] -> [a]
const reverse = xs =>
typeof xs === 'string' ? (
xs.split('')
.reverse()
.join('')
) : xs.slice(0)
.reverse();
 
// same :: a -> a -> Bool
const same = (a, b) => a === b
 
// show :: Int -> a -> Indented String
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[1], null, x[0]] : x
);
 
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
const sortBy = (f, xs) =>
xs.slice()
.sort(f);
 
// tail :: [a] -> [a]
const tail = xs => xs.length ? xs.slice(1) : undefined;
 
// take :: Int -> [a] -> [a]
const take = curry((n, xs) => xs.slice(0, n));
 
// toLower :: Text -> Text
const toLower = s => s.toLowerCase();
 
// TEST ------------------------------------------------------------------
 
const xs = [
["Employee Name", "Employee ID", "Salary", "Department"],
["Tyler Bennett", "E10297", "32000", "D101"],
["John Rappl", "E21437", "47000", "D050"],
["George Woltman", "E00127", "53500", "D101"],
["Adam Smith", "E63535", "18000", "D202"],
["Claire Buckman", "E39876", "27800", "D202"],
["David McClellan", "E04242", "41500", "D101"],
["Rich Holcomb", "E01234", "49500", "D202"],
["Nathan Adams", "E41298", "21900", "D050"],
["Richard Potter", "E43128", "15900", "D101"],
["David Motsinger", "E27002", "19250", "D202"],
["Tim Sampair", "E03033", "27000", "D101"],
["Kim Arlich", "E10001", "57000", "D190"],
["Timothy Grove", "E16398", "29900", "D190"]
];
 
return show(2,
topNSalariesPerDept(3, xs)
);
})();</syntaxhighlight>
{{Out}}
<pre>[
"John Rappl,E21437,47000,D050",
"Nathan Adams,E41298,21900,D050",
"George Woltman,E00127,53500,D101",
"David McClellan,E04242,41500,D101",
"Tyler Bennett,E10297,32000,D101",
"Kim Arlich,E10001,57000,D190",
"Timothy Grove,E16398,29900,D190",
"Rich Holcomb,E01234,49500,D202",
"Claire Buckman,E39876,27800,D202",
"David Motsinger,E27002,19250,D202"
]</pre>
 
=={{header|jq}}==
The task description invites use of the "language-native" data structure, which for jq is JSON, and so the following assumes that the file data.json contains an array of objects, each having as keys the strings on the header line. Thus, the first object in the array looks like this:
<syntaxhighlight lang="json"> {
"Employee Name": "Tyler Bennett",
"Employee ID": "E10297",
"Salary": "32000",
"Department": "D101"
}</syntaxhighlight>
===Program===
<syntaxhighlight lang="jq">def top_rank_per_department(n):
group_by(.Department)
| reduce .[] as $dept
([]; ($dept
| map(.Salary)
| sort # from least to most
| .[length - n:] # top n salaries
| reverse) as $max
| ($dept[0] | .Department) as $dept
| . + [ { "Department": $dept, "top_salaries": $max } ] );
</syntaxhighlight>
 
===Example===
With the above program, the top two salaries in each dapartment can be found as shown in the following transcript:<syntaxhighlight lang="json">
$ jq 'top_rank_per_department(2) data.json
[
{
"Department": "D050",
"top_salaries": [
"47000",
"21900"
]
},
{
"Department": "D101",
"top_salaries": [
"53500",
"41500"
]
},
{
"Department": "D190",
"top_salaries": [
"57000",
"29900"
]
},
{
"Department": "D202",
"top_salaries": [
"49500",
"27800"
]
}
]</syntaxhighlight>
 
=={{header|Jsish}}==
Based on Javascript, imperative solution
<syntaxhighlight lang="javascript">#!/usr/bin/env jsish
/* Top rank per group, in Jsish */
function top_rank(n) {
var by_dept = group_by_dept(data);
for (var dept in by_dept) {
puts(dept);
for (var i = 0; i < n && i < by_dept[dept].length; i++) {
var emp = by_dept[dept][i];
puts(emp.name + ", id=" + emp.id + ", salary=" + emp.salary);
}
puts("");
}
}
// group by dept, and sort by salary
function group_by_dept(data) {
var by_dept = {};
for (var idx in data) {
var dept = data[idx].dept;
if ( !by_dept.hasOwnProperty(dept)) {
by_dept[dept] = new Array();
}
by_dept[dept].push(data[idx]);
}
for (var dept in by_dept) {
by_dept[dept].sort(function (a,b) { return b.salary - a.salary; });
}
return by_dept;
}
if (Interp.conf('unitTest')) {
var data = [
{name: "Tyler Bennett", id: "E10297", salary: 32000, dept: "D101"},
{name: "John Rappl", id: "E21437", salary: 47000, dept: "D050"},
{name: "George Woltman", id: "E00127", salary: 53500, dept: "D101"},
{name: "Adam Smith", id: "E63535", salary: 18000, dept: "D202"},
{name: "Claire Buckman", id: "E39876", salary: 27800, dept: "D202"},
{name: "David McClellan", id: "E04242", salary: 41500, dept: "D101"},
{name: "Rich Holcomb", id: "E01234", salary: 49500, dept: "D202"},
{name: "Nathan Adams", id: "E41298", salary: 21900, dept: "D050"},
{name: "Richard Potter", id: "E43128", salary: 15900, dept: "D101"},
{name: "David Motsinger", id: "E27002", salary: 19250, dept: "D202"},
{name: "Tim Sampair", id: "E03033", salary: 27000, dept: "D101"},
{name: "Kim Arlich", id: "E10001", salary: 57000, dept: "D190"},
{name: "Timothy Grove", id: "E16398", salary: 29900, dept: "D190"}
];
top_rank(3);
}
 
/*
=!EXPECTSTART!=
D050
John Rappl, id=E21437, salary=47000
Nathan Adams, id=E41298, salary=21900
 
D101
George Woltman, id=E00127, salary=53500
David McClellan, id=E04242, salary=41500
Tyler Bennett, id=E10297, salary=32000
 
D190
Kim Arlich, id=E10001, salary=57000
Timothy Grove, id=E16398, salary=29900
 
D202
Rich Holcomb, id=E01234, salary=49500
Claire Buckman, id=E39876, salary=27800
David Motsinger, id=E27002, salary=19250
 
=!EXPECTEND!=
*/</syntaxhighlight>
 
{{out}}
<pre>prompt$ jsish --U topRankPerGroup.jsi
D050
John Rappl, id=E21437, salary=47000
Nathan Adams, id=E41298, salary=21900
 
D101
George Woltman, id=E00127, salary=53500
David McClellan, id=E04242, salary=41500
Tyler Bennett, id=E10297, salary=32000
 
D190
Kim Arlich, id=E10001, salary=57000
Timothy Grove, id=E16398, salary=29900
 
D202
Rich Holcomb, id=E01234, salary=49500
Claire Buckman, id=E39876, salary=27800
David Motsinger, id=E27002, salary=19250
 
prompt$ jsish -u topRankPerGroup.jsi
[PASS] topRankPerGroup.jsi</pre>
 
=={{header|Julia}}==
<syntaxhighlight lang="julia"># v0.6.0
 
using DataFrames
 
df = DataFrame(
EmployeeName=["Tyler Bennett", "John Rappl", "George Woltman", "Adam Smith",
"Claire Buckman", "David McClellan", "Rich Holcomb", "Nathan Adams",
"Richard Potter", "David Motsinger", "Tim Sampair", "Kim Arlich", "Timothy Grove"],
EmployeeID = ["E10297", "E21437", "E00127", "E63535", "E39876", "E04242",
"E01234", "E41298", "E43128", "E27002", "E03033", "E10001", "E16398"],
Salary = [32000, 47000, 53500, 18000, 27800, 41500, 49500, 21900, 15900, 19250,
27000, 57000, 29900],
Department = ["D101", "D050", "D101", "D202", "D202", "D101", "D202", "D050",
"D101", "D202", "D101", "D190", "D190"])
 
# To get only values
function firstnby(n::Int, y::Array, by::Array)
# Check that each value belong to one and one only class
if length(y) != length(by); error("y and by must have the same length"); end
 
# Initialize resulting dictionary
rst = Dict{eltype(by), Array{eltype(y)}}()
 
# For each class...
for cl in unique(by)
# ...select the values of that class...
i = find(x -> x == cl, by)
# ...sort them and store them in result...
rst[cl] = sort(y[i]; rev=true)
# ...if length is greater than n select only first n elements
if length(i) > n
rst[cl] = rst[cl][1:n]
end
end
return rst
end
 
for (cl, val) in firstnby(3, Array(df[:Salary]), Array(df[:Department]))
println("$cl => $val")
end
 
# To get the full row...
function firstnby(n::Int, df::DataFrame, y::Symbol, by::Symbol)
rst = Dict{eltype(df[by]), DataFrame}()
 
for cl in unique(df[by])
i = find(x -> x == cl, df[by])
rst[cl] = sort(df[i, :]; cols=order(y; rev=true))
if length(i) > n
rst[cl] = rst[cl][1:n, :]
end
end
return rst
end
 
for (cl, data) in firstnby(3, df, :Salary, :Department)
println("\n$cl:\n$data")
end
</syntaxhighlight>
 
{{out}}
<pre>D202 => [49500, 27800, 19250]
D101 => [53500, 41500, 32000]
D050 => [47000, 21900]
D190 => [57000, 29900]
 
D202:
3×4 DataFrames.DataFrame
│ Row │ EmployeeName │ EmployeeID │ Salary │ Department │
├─────┼───────────────────┼────────────┼────────┼────────────┤
│ 1 │ "Rich Holcomb" │ "E01234" │ 49500 │ "D202" │
│ 2 │ "Claire Buckman" │ "E39876" │ 27800 │ "D202" │
│ 3 │ "David Motsinger" │ "E27002" │ 19250 │ "D202" │
 
D101:
3×4 DataFrames.DataFrame
│ Row │ EmployeeName │ EmployeeID │ Salary │ Department │
├─────┼───────────────────┼────────────┼────────┼────────────┤
│ 1 │ "George Woltman" │ "E00127" │ 53500 │ "D101" │
│ 2 │ "David McClellan" │ "E04242" │ 41500 │ "D101" │
│ 3 │ "Tyler Bennett" │ "E10297" │ 32000 │ "D101" │
 
D050:
2×4 DataFrames.DataFrame
│ Row │ EmployeeName │ EmployeeID │ Salary │ Department │
├─────┼────────────────┼────────────┼────────┼────────────┤
│ 1 │ "John Rappl" │ "E21437" │ 47000 │ "D050" │
│ 2 │ "Nathan Adams" │ "E41298" │ 21900 │ "D050" │
 
D190:
2×4 DataFrames.DataFrame
│ Row │ EmployeeName │ EmployeeID │ Salary │ Department │
├─────┼─────────────────┼────────────┼────────┼────────────┤
│ 1 │ "Kim Arlich" │ "E10001" │ 57000 │ "D190" │
│ 2 │ "Timothy Grove" │ "E16398" │ 29900 │ "D190" │
</pre>
 
=={{header|Kotlin}}==
<syntaxhighlight lang="scala">// version 1.1.2
 
data class Employee(val name: String, val id: String, val salary: Int, val dept: String)
 
const val N = 2 //say
 
fun main(args: Array<String>) {
val employees = listOf(
Employee("Tyler Bennett", "E10297", 32000, "D101"),
Employee("John Rappl", "E21437", 47000, "D050"),
Employee("George Woltman" , "E00127", 53500, "D101"),
Employee("Adam Smith", "E63535", 18000, "D202"),
Employee("Claire Buckman", "E39876", 27800, "D202"),
Employee("David McClellan", "E04242", 41500, "D101"),
Employee("Rich Holcomb", "E01234", 49500, "D202"),
Employee("Nathan Adams", "E41298", 21900, "D050"),
Employee("Richard Potter", "E43128", 15900, "D101"),
Employee("David Motsinger", "E27002", 19250, "D202"),
Employee("Tim Sampair", "E03033", 27000, "D101"),
Employee("Kim Arlich", "E10001", 57000, "D190"),
Employee("Timothy Grove", "E16398", 29900, "D190")
)
val employeesByDept = employees.sortedBy { it.dept }.groupBy { it.dept }
println("Highest $N salaries by department:\n")
for ((key, value) in employeesByDept) {
val topRanked = value.sortedByDescending { it.salary }.take(N)
println("Dept $key => ")
for (i in 0 until N) with (topRanked[i]) { println("${name.padEnd(15)} $id $salary") }
println()
}
}</syntaxhighlight>
 
{{out}}
<pre>
Highest 2 salaries by department:
 
Dept D050 =>
John Rappl E21437 47000
Nathan Adams E41298 21900
 
Dept D101 =>
George Woltman E00127 53500
David McClellan E04242 41500
 
Dept D190 =>
Kim Arlich E10001 57000
Timothy Grove E16398 29900
 
Dept D202 =>
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
</pre>
 
=={{header|Ksh}}==
<syntaxhighlight lang="ksh">
#!/bin/ksh
exec 2> /tmp/Top_rank_per_group.err
 
# Top rank per group
 
# # Variables:
#
integer TOP_NUM=2
 
typeset -T Empsal_t=(
typeset -h 'Employee Name' ename=''
typeset -h 'Employee ID' eid=''
typeset -i -h 'Employee Salary' esalary
typeset -h 'Emplyee Department' edept=''
 
function init_employee {
typeset buff ; buff="$1"
 
typeset oldIFS ; oldIFS="$IFS"
typeset arr ; typeset -a arr
IFS=\,
arr=( ${buff} )
 
_.ename="${arr[0]}"
_.eid="${arr[1]}"
_.esalary=${arr[2]}
_.edept="${arr[3]}"
 
IFS="${oldIFS}"
}
)
 
edata='Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190'
 
######
# main #
######
 
# # Employee data into array of Types
#
typeset -a empsal_t # array of Type variables (objects)
integer j i=0
echo "${edata}" | while read; do
Empsal_t empsal_t[i] # Create Type (object)
empsal_t[i++].init_employee "$REPLY" # Initialize Type (object)
done
 
# # Sort the array of Type variables
#
set -a -s -A empsal_t -K edept,esalary:n:r,ename
 
# # BUG work around! duplicate the now sorted Type array and use it for output
#
sorted=$(typeset -p empsal_t) && sorted=${sorted/empsal_t/sorted} && eval ${sorted}
 
for ((i=0; i<${#sorted[*]}; i++)); do
if [[ ${sorted[i].edept} != ${prevdept} ]] || (( j < TOP_NUM )); then
[[ ${sorted[i].edept} != ${prevdept} ]] && j=0
print "${sorted[i].edept} ${sorted[i].esalary} ${sorted[i].eid} ${sorted[i].ename}"
prevdept=${sorted[i].edept}
(( j++ ))
fi
done</syntaxhighlight>
{{out}}<pre>
D050 47000 E21437 John Rappl
D050 21900 E41298 Nathan Adams
D101 53500 E00127 George Woltman
D101 41500 E04242 David McClellan
D190 57000 E10001 Kim Arlich
D190 29900 E16398 Timothy Grove
D202 49500 E01234 Rich Holcomb
D202 27800 E39876 Claire Buckman</pre>
 
=={{header|Lua}}==
<langsyntaxhighlight lang="lua">N = 2
 
lst = { { "Tyler Bennett","E10297",32000,"D101" },
{ "John Rappl","E21437",47000,"D050" },
{ "George Woltman","E00127",53500,"D101" },
{ "Adam Smith","E63535",18000,"D202" },
{ "Claire Buckman","E39876",27800,"D202" },
{ "David McClellan","E04242",41500,"D101" },
{ "Rich Holcomb","E01234",49500,"D202" },
{ "Nathan Adams","E41298",21900,"D050" },
{ "Richard Potter","E43128",15900,"D101" },
{ "David Motsinger","E27002",19250,"D202" },
{ "Tim Sampair","E03033",27000,"D101" },
{ "Kim Arlich","E10001",57000,"D190" },
{ "Timothy Grove","E16398",29900,"D190" }
}
 
Line 1,425 ⟶ 3,797:
for i = 1, #lst do
if dep[ lst[i][4] ] == nil then
dep[ lst[i][4] ] = {}
dep[ lst[i][4] ][1] = lst[i]
else
dep[ lst[i][4] ][#dep[lst[i][4]]+1] = lst[i]
end
end
Line 1,434 ⟶ 3,806:
for i, _ in pairs( dep ) do
table.sort( dep[i], function (a,b) return a[3] > b[3] end )
print( "Department:", dep[i][1][4] )
for l = 1, math.min( N, #dep[i] ) do
print( "", dep[i][l][1], dep[i][l][2], dep[i][l][3] )
end
print ""
end </langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department: D050
Output:
John Rappl E21437 47000
<pre>Department: D050
Nathan Adams E41298 21900
John Rappl E21437 47000
Nathan Adams E41298 21900
 
Department: D202
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
 
Department: D190
Kim Arlich E10001 57000
Timothy Grove E16398 29900
 
Department: D101
George Woltman E00127 53500
David McClellan E04242 41500</pre>
 
=== Object-Oriented ===
<syntaxhighlight lang="lua">--Employee Class
local EmployeeMethods = {
}
local EmployeeMetamethods = {
__index=EmployeeMethods,
__tostring=function(self)
return ("%s %s %s %s"):format(self.Name,self.EmployeeId,self.Salary,self.DepartmentName)
end,
__metatable="Locked",
}
local EmployeeBase = {}
 
EmployeeBase.new = function(Name,EmployeeId,Salary,DepartmentName)
return setmetatable({
Name=Name,
EmployeeId=EmployeeId,
Salary=Salary,
DepartmentName=DepartmentName,
},EmployeeMetamethods)
end
 
--Department Class
local DepartmentMethods = {
NewEmployee=function(self,Employee)
table.insert(self.__Employees,Employee)
end,
CalculateHighestSalaries=function(self,Amount)
local Highest = {}
local EL = #self.__Employees
table.sort(self.__Employees,function(a,b)
return a.Salary > b.Salary
end)
for i=1,Amount do
if i>EL then
break
end
table.insert(Highest,self.__Employees[i])
end
return Highest
end,
}
local DepartmentMetamethods = {
__index=DepartmentMethods,
__tostring=function(self)
return ("Department %s"):format(self.Name)
end,
__metatable="Locked",
}
local DepartmentBase = {
__Departments={},
}
 
DepartmentBase.new = function(Name)
local Department = DepartmentBase.__Departments[Name]
if Department then return Department end
Department = setmetatable({
__Employees={},
Name=Name,
},DepartmentMetamethods)
DepartmentBase.__Departments[Name] = Department
return Department
end
 
--Main Program
 
local Employees = {
EmployeeBase.new("Tyler Bennett","E10297",32000,"D101"),
EmployeeBase.new("John Rappl","E21437",47000,"D050"),
EmployeeBase.new("George Woltman","E00127",53500,"D101"),
EmployeeBase.new("Adam Smith","E63535",18000,"D202"),
EmployeeBase.new("Claire Buckman","E39876",27800,"D202"),
EmployeeBase.new("David McClellan","E04242",41500,"D101"),
EmployeeBase.new("Rich Holcomb","E01234",49500,"D202"),
EmployeeBase.new("Nathan Adams","E41298",21900,"D050"),
EmployeeBase.new("Richard Potter","E43128",15900,"D101"),
EmployeeBase.new("David Motsinger","E27002",19250,"D202"),
EmployeeBase.new("Tim Sampair","E03033",27000,"D101"),
EmployeeBase.new("Kim Arlich","E10001",57000,"D190"),
EmployeeBase.new("Timothy Grove","E16398",29900,"D190"),
}
 
for _,Employee in next,Employees do
local Department = DepartmentBase.new(Employee.DepartmentName)
Department:NewEmployee(Employee)
end
 
for _,Department in next,DepartmentBase.__Departments do
local Highest = Department:CalculateHighestSalaries(2)
print(Department)
for _,Employee in next,Highest do
print("\t"..tostring(Employee))
end
end</syntaxhighlight>
{{out}}
<pre>
Department D050
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
Department D101
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Department D190
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
Department D202
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
</pre>
 
=={{header|M2000 Interpreter}}==
<syntaxhighlight lang="m2000 interpreter">
Module Checkit {
' erase stack of values, so we can add data
Flush
Input "N=",N
Enum Departments {D050,D101,D190,D202}
 
\\ Inventory Department need unique keys
Inventory Department
\\ each item in this inventory should be an inventory too
Class Empl {
name$, id$, salary
Class:
Module Empl(.name$, .id$, .salary) {}
}
Data "Tyler Bennett","E10297",32000,D101
Data "John Rappl","E21437",47000,D050
Data "George Woltman","E00127",53500,D101
Data "Adam Smith","E63535",18000,D202
Data "Claire Buckman","E39876",27800,D202
Data "David McClellan","E04242",41500,D101
Data "Rich Holcomb","E01234",49500,D202
Data "Nathan Adams","E41298",21900,D050
Data "Richard Potter","E43128",15900,D101
Data "David Motsinger","E27002",19250,D202
Data "Tim Sampair","E03033",27000,D101
Data "Kim Arlich","E10001",57000,D190
Data "Timothy Grove","E16398",29900,D190
Data ""
Read name$
While name$<>"" {
Read id$, salary, dep
Rem : Print name$, id$, salary, dep
If Exist(Department, dep) Then {
z=Eval(Department) ' get pointer to inventory
AppendOne()
} Else {
z=queue
AppendDep()
AppendOne()
}
Read name$
}
Sort Department as number
i=each(Department)
\\ make depname as type of Departments
depname=D050
Print "Dep. Employee Name Emp. ID Salary"
While i {
\\ when we pass a number to a enum variable
\\ if the number exist, get that enum item else raise error
depname=val(eval$(i, i^))
\\ z is a pointer to inventory
z=Eval(i)
Sort descending z as number
k=each(z,1,N)
While k {
Empl=Eval(k)
For Empl {
\\ eval$(depname) return the name of enum variable (like D050)
Print Format$("{0:6}{1:20}{2:8}{3::-8}",Eval$(depname), .name$, .id$, .salary)
}
}
}
Print "Done"
Sub AppendDep()
Append Department, dep:=z
End Sub
Sub AppendOne()
Append z, salary:=Empl(name$, id$, salary)
End Sub
}
Checkit
</syntaxhighlight>
{{out}}
<pre style="height:30ex;overflow:scroll">
N=1
Dep. Employee Name Emp. ID Salary
D050 John Rappl E21437 47000
D101 George Woltman E00127 53500
D190 Kim Arlich E10001 57000
D202 Rich Holcomb E01234 49500
Done
 
N=2
Department: D101
Dep. Employee Name Emp. ID Salary
George Woltman E00127 53500
D050 John Rappl E21437 47000
David McClellan E04242 41500</pre>
D050 Nathan Adams E41298 21900
D101 George Woltman E00127 53500
D101 David McClellan E04242 41500
D190 Kim Arlich E10001 57000
D190 Timothy Grove E16398 29900
D202 Rich Holcomb E01234 49500
D202 Claire Buckman E39876 27800
Done
</pre >
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<langsyntaxhighlight Mathematicalang="mathematica">InitialList ={{"Tyler Bennett","E10297",32000,"D101"},
{"John Rappl","E21437",47000,"D050"},{"George Woltman","E00127",53500,"D101"},
{"Adam Smith","E63535",18000,"D202"},{"Claire Buckman","E39876",27800,"D202"},
Line 1,472 ⟶ 4,052:
 
Scan[((Print["Department ",#[[1,4]],"\n","Employee","\t","Id","\t","Salary"]&[#])&[#];
(Scan[Print[#[[1]],"\t",#[[2]],"\t",#[[3]]]&,#] )& [#])&,TrimmedList]</langsyntaxhighlight>
{{out}}
 
<pre style="height:30ex;overflow:scroll"><pre>Department D101
<pre>Example:
Employee Id Salary
Department D101
George Woltman E00127 53500
Employee Id Salary
David McClellan E04242 41500
George Woltman E00127 53500
Tyler Bennett E10297 32000
David McClellan E04242 41500
Tyler Bennett E10297 32000
Department D050
Employee Id Salary
John Rappl E21437 47000
Nathan Adams E41298 21900
Department D202
Employee Id Salary
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
David Motsinger E27002 19250
Department D190
Employee Id Salary
Kim Arlich E10001 57000
Timothy Grove E16398 29900</pre>
 
=={{header|Nim}}==
{{trans|C}}
<syntaxhighlight lang="nim">import algorithm
 
type Record = tuple[name, id: string; salary: int; department: string]
 
var people = [("Tyler Bennett", "E10297", 32000, "D101"),
("John Rappl", "E21437", 47000, "D050"),
("George Woltman", "E00127", 53500, "D101"),
("Adam Smith", "E63535", 18000, "D202"),
("Claire Buckman", "E39876", 27800, "D202"),
("David McClellan", "E04242", 41500, "D101"),
("Rich Holcomb", "E01234", 49500, "D202"),
("Nathan Adams", "E41298", 21900, "D050"),
("Richard Potter", "E43128", 15900, "D101"),
("David Motsinger", "E27002", 19250, "D202"),
("Tim Sampair", "E03033", 27000, "D101"),
("Kim Arlich", "E10001", 57000, "D190"),
("Timothy Grove", "E16398", 29900, "D190")]
 
proc pcmp(a, b: Record): int =
result = cmp(a.department, b.department)
if result == 0:
result = cmp(b.salary, a.salary)
 
proc printTop(people: openArray[Record]; n: Positive) =
var people = sorted(people, pcmp)
var rank = 0
for i, p in people:
if i > 0 and p.department != people[i-1].department:
rank = 0
echo()
if rank < n:
echo p.department, " ", p.salary, " ", p.name
inc rank
 
people.printTop(2)</syntaxhighlight>
 
{{out}}
<pre>D050 47000 John Rappl
D050 21900 Nathan Adams
 
D101 53500 George Woltman
D101 41500 David McClellan
 
D190 57000 Kim Arlich
D190 29900 Timothy Grove
 
D202 49500 Rich Holcomb
D202 27800 Claire Buckman</pre>
 
=={{header|OCaml}}==
<langsyntaxhighlight lang="ocaml">open StdLabels
 
let to_string (name,_,s,_) = (Printf.sprintf "%s (%d)" name s)
Line 1,545 ⟶ 4,174:
let () =
toprank data 3;
;;</syntaxhighlight>{{out}}
;;</lang>
<pre style="height:30ex;overflow:scroll">Department: D190
 
outputs:
<pre>Department: D190
Kim Arlich (57000), Timothy Grove (29900)
 
Line 1,561 ⟶ 4,188:
</pre>
--[[User:Bbsingapore|Bbsingapore]] 10:00, 13 January 2012 (UTC)
 
=={{header|Oforth}}==
 
First sort by department. Then group elements with same department.
 
Then sort each department by salaries and keep only the first n elements.
 
<syntaxhighlight lang="oforth">Object Class new: Employee(name, id, salary, dep)
Employee method: initialize := dep := salary := id := name ;
Employee method: salary @salary ;
Employee method: dep @dep ;
Employee method: << "[" << @dep << "," << @name << "," << @salary << "]" << ;
: topRank(n)
| employees |
ListBuffer new ->employees
Employee new("Tyler Bennett", "E10297", 32000, "D101") employees add
Employee new("John Rappl", "E21437", 47000, "D050") employees add
Employee new("George Woltman", "E00127", 53500, "D101") employees add
Employee new("Adam Smith", "E63535", 18000, "D202") employees add
Employee new("Claire Buckman", "E39876", 27800, "D202") employees add
Employee new("David McClellan", "E04242", 41500, "D101") employees add
Employee new("Rich Holcomb", "E01234", 49500, "D202") employees add
Employee new("Nathan Adams", "E41298", 21900, "D050") employees add
Employee new("Richard Potter", "E43128", 15900, "D101") employees add
Employee new("David Motsinger", "E27002", 19250, "D202") employees add
Employee new("Tim Sampair", "E03033", 27000, "D101") employees add
Employee new("Kim Arlich", "E10001", 57000, "D190") employees add
Employee new("Timothy Grove", "E16398", 29900, "D190") employees add
#dep employees sortBy groupWith( #dep )
map(#[ sortBy(#[ salary neg ]) left(n) ]) apply(#println) ; </syntaxhighlight>
 
{{out}}
<pre>
>topRank(3)
[[D050,John Rappl,47000], [D050,Nathan Adams,21900]]
[[D101,George Woltman,53500], [D101,David McClellan,41500], [D101,Tyler Bennett,32000]]
[[D190,Kim Arlich,57000], [D190,Timothy Grove,29900]]
[[D202,Rich Holcomb,49500], [D202,Claire Buckman,27800], [D202,David Motsinger,19250]]
</pre>
 
=={{header|Oz}}==
<langsyntaxhighlight lang="oz">declare
%% Create a list of employee records.
Data = {Map
Line 1,587 ⟶ 4,257:
{Record.map {GroupBy Employees department}
fun {$ Employees}
{List.take
{Sort Employees CompareSalary}
N}
end}
end
Line 1,608 ⟶ 4,278:
end
in
{Inspect {TopEarners Data 3}}</langsyntaxhighlight>
 
=={{header|PARI/GP}}==
{{PARI/GP select}}
<langsyntaxhighlight lang="parigp">{V=[["Tyler Bennett","E10297",32000,"D101"],
["John Rappl","E21437",47000,"D050"],
["George Woltman","E00127",53500,"D101"],
Line 1,639 ⟶ 4,309:
};
 
top(2,V)</langsyntaxhighlight>
 
=={{header|Pascal}}==
{{works with|Free_Pascal}}
{{libheader|Classes}} {{libheader|Math}}
<langsyntaxhighlight lang="pascal">program TopRankPerGroup(output);
 
uses
Line 1,652 ⟶ 4,322:
TData = record
name: string;
ID: string;
salary: longint;
dept: string
end;
PTData = ^TData;
 
Line 1,713 ⟶ 4,383:
end;
end.
</syntaxhighlight>{{out}}
</lang>
<pre style="height:30ex;overflow:scroll">% ./TopRankPerGroup
Output:
<pre>
% ./TopRankPerGroup
Enter the number of ranks: 3
 
Line 1,738 ⟶ 4,406:
 
=={{header|Perl}}==
<langsyntaxhighlight lang="perl">sub zip {
my @a = @{shift()};
my @b = @{shift()};
Line 1,770 ⟶ 4,438:
EOF
 
my $N = shift || 3;
@ARGV or die "Please provide a value for N.\n";
my $N = shift;
 
foreach my $d (sort uniq sort map {$_->{dept}} @data) {
print "$d\n";
my @es =
Line 1,785 ⟶ 4,452:
}
print "\n";
}</langsyntaxhighlight>
{{out}}
<pre>D050
John Rappl | E21437 | 47000
Nathan Adams | E41298 | 21900
 
D101
=={{header|Perl 6}}==
George Woltman | E00127 | 53500
David McClellan | E04242 | 41500
Tyler Bennett | E10297 | 32000
 
D190
{{works with|Rakudo|2010.09.07}}
Kim Arlich | E10001 | 57000
Timothy Grove | E16398 | 29900
 
D202
The <tt>Z=></tt> operator zips two lists into a list of pairs. The <tt>classify</tt> method generates pairs where each key is a different department, and each value all the entries in that department.We then sort the pairs and process each department separately. Within each department, we sort on salary (negated to reverse the order). The last statement is essentially a list comprehension that uses a slice subscript with the <tt>^</tt> "up to" operator to take the first N elements of the sorted employee list. (The <tt>if .so</tt> will eventually not be necessary since range subscripts are supposed to autotruncate ranges that run off the end of an array, but rakudo does not yet implement this.) The <tt>.<...></tt> form is a slice hash subscript with literals strings. That in turn is just the subscript form of the <tt><...></tt> "quote words" form, which is more familar to Perl 5 programmers as
Rich Holcomb | E01234 | 49500
<tt>qw/.../</tt>. We used that form first in the initial data set.
Claire Buckman | E39876 | 27800
David Motsinger | E27002 | 19250</pre>
 
=={{header|Phix}}==
This program also makes heavy use of method calls that start with dot. In Perl&nbsp;6 this means a method call on the current topic, <tt>$_</tt>, which is automatically set by any looping or map construct that doesn't declare an explicit formal parameter on its closure.
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">N</span><span style="color: #0000FF;">=</span><span style="color: #000000;">3</span>
<span style="color: #000080;font-style:italic;">-- Employee Name,Employee ID,Salary,Department</span>
<span style="color: #008080;">enum</span> <span style="color: #000080;font-style:italic;">/*NAME,*/ /*ID,*/</span> <span style="color: #000000;">SAL</span><span style="color: #0000FF;">=</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">DEPT</span><span style="color: #0000FF;">=</span><span style="color: #000000;">4</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">employees</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #008000;">"Tyler Bennett"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E10297"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">32000</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D101"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"John Rappl"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E21437"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">47000</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D050"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"George Woltman"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E00127"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">53500</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D101"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Adam Smith"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E63535"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">18000</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D202"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Claire Buckman"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E39876"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">27800</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D202"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"David McClellan"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"E04242"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">41500</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D101"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Rich Holcomb"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E01234"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">49500</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D202"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Nathan Adams"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E41298"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">21900</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D050"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Richard Potter"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E43128"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">15900</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D101"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"David Motsinger"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"E27002"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">19250</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D202"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Tim Sampair"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E03033"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">27000</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D101"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Kim Arlich"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E10001"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">57000</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D190"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Timothy Grove"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"E16398"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">29900</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"D190"</span><span style="color: #0000FF;">}}</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">by_dept_sal</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #7060A8;">compare</span><span style="color: #0000FF;">(</span><span style="color: #000000;">employees</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">DEPT</span><span style="color: #0000FF;">]&-</span><span style="color: #000000;">employees</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">SAL</span><span style="color: #0000FF;">],</span>
<span style="color: #000000;">employees</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">][</span><span style="color: #000000;">DEPT</span><span style="color: #0000FF;">]&-</span><span style="color: #000000;">employees</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">][</span><span style="color: #000000;">SAL</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">tags</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">custom_sort</span><span style="color: #0000FF;">(</span><span style="color: #000000;">by_dept_sal</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">employees</span><span style="color: #0000FF;">)))</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">lastdep</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">dcount</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Top %d salaries by department\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">N</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">employees</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">object</span> <span style="color: #000000;">emp</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">employees</span><span style="color: #0000FF;">[</span><span style="color: #000000;">tags</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">emp</span><span style="color: #0000FF;">[</span><span style="color: #000000;">DEPT</span><span style="color: #0000FF;">]!=</span><span style="color: #000000;">lastdep</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">lastdep</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">emp</span><span style="color: #0000FF;">[</span><span style="color: #000000;">DEPT</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">dcount</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">dcount</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">dcount</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">N</span> <span style="color: #008080;">then</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">emp</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{Out}}
<pre>
Top 3 salaries by department
 
{"John Rappl","E21437",47000,"D050"}
<lang perl6>my @data = (
{"Nathan Adams","E41298",21900,"D050"}
[< E10297 32000 D101 Tyler_Bennett >],
[< E21437 47000 D050 John_Rappl >],
[< E00127 53500 D101 George_Woltman >],
[< E63535 18000 D202 Adam_Smith >],
[< E39876 27800 D202 Claire_Buckman >],
[< E04242 41500 D101 David_McClellan >],
[< E01234 49500 D202 Rich_Holcomb >],
[< E41298 21900 D050 Nathan_Adams >],
[< E43128 15900 D101 Richard_Potter >],
[< E27002 19250 D202 David_Motsinger >],
[< E03033 27000 D101 Tim_Sampair >],
[< E10001 57000 D190 Kim_Arlich >],
[< E16398 29900 D190 Timothy_Grove >],
).map: { item %( < id salary dept name > Z=> .list ) }
 
{"George Woltman","E00127",53500,"D101"}
sub MAIN($N as Int = 3) {
{"David McClellan","E04242",41500,"D101"}
my $cont = 0;
{"Tyler Bennett","E10297",32000,"D101"}
for @data.classify({ .<dept> }).sort».value {
my @es = .sort({ -.<salary> });
print "\n" if $cont++;
say .< dept id salary name >.Str if .so for @es[^$N];
}
}</lang>
 
{"Kim Arlich","E10001",57000,"D190"}
Output:<pre>
{"Timothy Grove","E16398",29900,"D190"}
D050 E21437 47000 John_Rappl
D050 E41298 21900 Nathan_Adams
 
{"Rich Holcomb","E01234",49500,"D202"}
D101 E00127 53500 George_Woltman
{"Claire Buckman","E39876",27800,"D202"}
D101 E04242 41500 David_McClellan
{"David Motsinger","E27002",19250,"D202"}
D101 E10297 32000 Tyler_Bennett
</pre>
 
D190 E10001 57000 Kim_Arlich
D190 E16398 29900 Timothy_Grove
 
D202 E01234 49500 Rich_Holcomb
D202 E39876 27800 Claire_Buckman
D202 E27002 19250 David_Motsinger</pre>
 
=={{header|PHP}}==
<langsyntaxhighlight lang="php">$data = Array(
Array("Tyler Bennett","E10297",32000,"D101"),
Array("John Rappl","E21437",47000,"D050"),
Array("George Woltman","E00127",53500,"D101"),
Array("Adam Smith","E63535",18000,"D202"),
Array("Claire Buckman","E39876",27800,"D202"),
Array("David McClellan","E04242",41500,"D101"),
Array("Rich Holcomb","E01234",49500,"D202"),
Array("Nathan Adams","E41298",21900,"D050"),
Array("Richard Potter","E43128",15900,"D101"),
Array("David Motsinger","E27002",19250,"D202"),
Array("Tim Sampair","E03033",27000,"D101"),
Array("Kim Arlich","E10001",57000,"D190"),
Array("Timothy Grove","E16398",29900,"D190")
);
);
function top_sal($num){
global $data;
$depts = Array();
foreach($data as $key => $arr){
if(!isset($depts[$arr[3]])) $depts[$arr[3]] = Array();
$depts[$arr[3]][] = $key;
}
}
function topsalsort($a,$b){
global $data;
if ($data[$a][2] == $data[$b][2]) {
return 0;
}
}
return ($data[$a][2] < $data[$b][2]) ? 1 : -1;
}
}
foreach ($depts as $key => $val){
usort($depts[$key],"topsalsort");
}
}
ksort($depts);
echo '<pre>';
foreach ($depts as $key => $val){
echo $key . '<br>';
echo 'Name ID Salary<br>';
$count = 0;
foreach($val as $value){
echo $data[$value][0] . ' ' . $data[$value][1] . ' ' . $data[$value][2] . '<br>';
$count++;
if($count>=$num) break;
}
}
echo '<br>';
}
}
echo '</pre>';
}
top_sal(3);</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">D050
<pre>
Name ID Salary
D050
John Rappl E21437 47000
Name ID Salary
Nathan Adams E41298 21900
John Rappl E21437 47000
Nathan Adams E41298 21900
 
D101
Name ID Salary
George Woltman E00127 53500
David McClellan E04242 41500
Tyler Bennett E10297 32000
 
D190
Name ID Salary
Kim Arlich E10001 57000
Timothy Grove E16398 29900
 
D202
Name ID Salary
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
David Motsinger E27002 19250
</pre>
 
=={{header|Picat}}==
<syntaxhighlight lang="picat">go =>
Emp = [
% Employee Name,Employee ID,Salary,Department
["Tyler Bennett","E10297","32000","D101"],
["John Rappl","E21437","47000","D050"],
["George Woltman","E00127","53500","D101"],
["Adam Smith","E63535","18000","D202"],
["Claire Buckman","E39876","27800","D202"],
["David McClellan","E04242","41500","D101"],
["Rich Holcomb","E01234","49500","D202"],
["Nathan Adams","E41298","21900","D050"],
["Richard Potter","E43128","15900","D101"],
["David Motsinger","E27002","19250","D202"],
["Tim Sampair","E03033","27000","D101"],
["Kim Arlich","E10001","57000","D190"],
["Timothy Grove","E16398","29900","D190"]
],
print_top_ranks(Emp,3),
nl.
 
print_top_ranks(Emp,N) =>
printf("Top %d ranks per department:\n", N),
foreach(Dept in [Dept : [_,_,_,Dept] in Emp].sort_remove_dups())
printf("Department %s\n", Dept),
Emp2 = sort_down([[Salary,Name] : [Name,_,Salary,D] in Emp, D = Dept]),
foreach({[Salary,Name],E} in zip(Emp2,1..Emp2.length), E <= N)
printf("%-20s %-10s\n", Name, Salary)
end,
nl
end.</syntaxhighlight>
 
{{out}}
<pre>Top 3 ranks per department:
Department D050
John Rappl 47000
Nathan Adams 21900
 
Department D101
George Woltman 53500
David McClellan 41500
Tyler Bennett 32000
 
Department D190
Kim Arlich 57000
Timothy Grove 29900
 
Department D202
Rich Holcomb 49500
Claire Buckman 27800
David Motsinger 19250</pre>
 
===Using data as facts===
<syntaxhighlight lang="picat">go2 =>
Emp = findall([Name,ID,Salary,Dept],emp(Name,ID,Salary,Dept)),
print_top_ranks(Emp,3),
nl.
 
emp("Tyler Bennett","E10297","32000","D101").
emp("John Rappl","E21437","47000","D050").
emp("George Woltman","E00127","53500","D101").
emp("Adam Smith","E63535","18000","D202").
emp("Claire Buckman","E39876","27800","D202").
emp("David McClellan","E04242","41500","D101").
emp("Rich Holcomb","E01234","49500","D202").
emp("Nathan Adams","E41298","21900","D050").
emp("Richard Potter","E43128","15900","D101").
emp("David Motsinger","E27002","19250","D202").
emp("Tim Sampair","E03033","27000","D101").
emp("Kim Arlich","E10001","57000","D190").
emp("Timothy Grove","E16398","29900","D190").</syntaxhighlight>
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp"># Employee Name, ID, Salary, Department
(de *Employees
("Tyler Bennett" E10297 32000 D101)
Line 1,935 ⟶ 4,707:
(prinl) ) ) )
 
(topEmployees 3)</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department D101:
Output:
<pre>Department D101:
Name ID Salary
1. George Woltman E00127 53500
Line 1,958 ⟶ 4,729:
1. Kim Arlich E10001 57000
2. Timothy Grove E16398 29900</pre>
 
=={{header|PL/I}}==
<syntaxhighlight lang="pl/i">(subrg, stringrange, stringsize):
rank: procedure options (main); /* 10 November 2013 */
 
declare 1 employee (13),
2 name char (15) varying,
2 ID char (6),
2 salary fixed (5),
2 department char (4);
declare done(hbound(employee)) bit (1);
declare ptr(hbound(employee)) fixed binary;
declare true bit(1) value ('1'b), false bit(1) value ('0'b);
declare dept character (4);
declare text character (80) varying;
declare (i, j, l, k, m, n, p, t) fixed binary;
declare in file input;
 
open file (in) title ('/TOP-RANK.DAT, RECSIZE(80), TYPE(TEXT)' );
 
on endfile (in) go to completed_input;
j = 0;
do forever;
get file (in) edit (text) (L);
j = j + 1;
i = index(text, ',');
name(j) = substr(text, 1, i-1);
k = index(text, ',', i+1);
ID(j) = substr(text, i+1, k-(i+1));
i = k; k = index(text, ',', i+1);
salary(j) = substr(text, i+1, k-(i+1));
department(j) = substr(text, k+1);
end;
 
completed_input:
m = hbound(employee);
put skip list ('How many highest-paid employees do you want?');
get (n);
put skip edit ('Looking for the ', trim(n),
' highest-paid employees in each department') (a);
done = false;
do i = 1 to m;
do j = 1 to m;
if done(j) then iterate;
dept = department(j);
/* done(j) = true; */
leave;
end;
/* Locate all the employees of this department. */
k = 0;
do j = 1 to m;
if ^done(j) & (department(j) = dept) then
do;
k = k + 1;
ptr(k) = j;
done(j) = true;
end;
end;
if k = 0 then leave; /* (No more departments.) */
 
put skip list ('Employees in department ' || dept || ' are:-' );
do j = 1 to k;
put skip list (employee(ptr(j)));
end;
/* We now have k employees in "dept". Now find the maximum n salaries. */
/* ptr points to all of them. */
/* Use a bubble sort to move n values to one end. */
do p = 1 to min(n, k);
do j = 1 to k-1;
if salary(ptr(j)) > salary(ptr(j+1)) then
do;
t = ptr(j+1); ptr(j+1) = ptr(j); ptr(j) = t;
end;
end;
end;
 
/* Having moved the largest n values to the end of our list, */
/* we print them. */
put skip list ('Highest-paid employees in department ' || dept || ':-');
do j = k to k-min(k,n)+1 by -1;
put skip list (employee(ptr(j)) );
end;
end;
put skip list ('FINISHED');
end rank;</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">How many highest-paid employees do you want?
 
Looking for the 2 highest-paid employees in each department
Employees in department D101 are:-
Tyler Bennett E10297 32000 D101
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Richard Potter E43128 15900 D101
Tim Sampair E03033 27000 D101
Highest-paid employees in department D101:-
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Employees in department D050 are:-
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
Highest-paid employees in department D050:-
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
Employees in department D202 are:-
Adam Smith E63535 18000 D202
Claire Buckman E39876 27800 D202
Rich Holcomb E01234 49500 D202
David Motsinger E27002 19250 D202
Highest-paid employees in department D202:-
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
Employees in department D190 are:-
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
Highest-paid employees in department D190:-
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
FINISHED</pre>
 
=={{header|PL/SQL}}==
<syntaxhighlight lang="plsql">create or replace procedure "Top rank per group"(TOP_N in pls_integer default 3) as
cursor CSR_EMP(TOP_N pls_integer) is
select case LINE
when 10 then
'Tot.' || LPAD(POPULATION, 2) || ' Employees in ' || TIE_COUNT ||
' deps.Avg salary:' || TO_CHAR(SALARY, '99990.99')
when 30 then
'-'
when 50 then
'Department: ' || DEPT_ID || ', pop: ' || POPULATION ||
'. Avg Salary: ' || TO_CHAR(SALARY, '99990.99')
when 70 then
LPAD('Employee ID', 14) || LPAD('Employee name', 20) ||
LPAD('Salary', 9) || 'Rank'
when 90 then
LPAD('+', 14, '-') || LPAD('+', 20, '-') || LPAD('+', 9, '-') ||
LPAD('+', 4, '-')
else
LPAD(' ', 8) || LPAD(EMP_ID, 6) || LPAD(EMP_NAME, 20) ||
TO_CHAR(SALARY, '99990.99') ||
LPAD(case when TIE_COUNT = 1 then ' ' else 'T' end || RANK, 4)
end "Top rank per group"
from (select 10 LINE
,null EMP_ID
,null EMP_NAME
,' ' DEPT_ID
,avg(SALARY) SALARY
,0 RANK
,count(distinct DEPT_ID) TIE_COUNT
,count(*) POPULATION
from EMP
union all
select 30 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 50 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,avg(SALARY) SALARY
,0 RANK
,0 TIE_COUNT
,count(*) POPULATION
from EMP
group by DEPT_ID
union all
select 70 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 90 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 110 LINE
,EMP_ID
,EMP_NAME
,DEPT_ID
,SALARY
,(select count(distinct EMP4.SALARY)
from EMP EMP4
where EMP4.DEPT_ID = EMP3.DEPT_ID
and EMP4.SALARY >= EMP3.SALARY) RANK
,(select count(*)
from EMP EMP2
where EMP2.DEPT_ID = EMP3.DEPT_ID
and EMP2.SALARY = EMP3.SALARY) TIE_COUNT
,0 POPULATION
from EMP EMP3
where TOP_N >= -- Here is the meat, Correlated subquery
(select count(distinct EMP4.SALARY)
from EMP EMP4
where EMP4.DEPT_ID = EMP3.DEPT_ID
and EMP4.SALARY >= EMP3.SALARY))
order by DEPT_ID
,LINE
,SALARY desc
,EMP_ID;
 
V_EMP CSR_EMP%rowtype;
begin
for V_EMP in CSR_EMP(TOP_N)
loop
DBMS_OUTPUT.PUT_LINE(v_emp."Top rank per group");
end loop;
end;</syntaxhighlight>
 
=={{header|PowerShell}}==
<langsyntaxhighlight lang="powershell">function New-Employee ($Name, $ID, $Salary, $Department) {
New-Object PSObject `
| Add-Member -PassThru NoteProperty EmployeeName $Name `
Line 1,991 ⟶ 4,990:
} `
| Format-Table -GroupBy Department
}</syntaxhighlight>{{out}}
}</lang>
<pre style="height:30ex;overflow:scroll">PS> Get-TopRank 2
Output:
<pre>PS> Get-TopRank 2
 
Department: D101
Line 2,022 ⟶ 5,020:
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190</pre>
 
=={{header|Prolog}}==
<syntaxhighlight lang="prolog">% emp(name,id,salary,dpt)
emp('Tyler Bennett','E10297',32000,'D101').
emp('John Rappl','E21437',47000,'D050').
emp('George Woltman','E00127',53500,'D101').
emp('Adam Smith','E63535',18000,'D202').
emp('Claire Buckman','E39876',27800,'D202').
emp('David McClellan','E04242',41500,'D101').
emp('Rich Holcomb','E01234',49500,'D202').
emp('Nathan Adams','E41298',21900,'D050').
emp('Richard Potter','E43128',15900,'D101').
emp('David Motsinger','E27002',19250,'D202').
emp('Tim Sampair','E03033',27000,'D101').
emp('Kim Arlich','E10001',57000,'D190').
emp('Timothy Grove','E16398',29900,'D190').
 
departments(Depts) :- % Find the set of departments
findall(Dpt, emp(_,_,_,Dpt), DList), list_to_set(DList, Depts).
 
greater(emp(_,_,Sal1,_), emp(_,_,Sal2,_)) :-
Sal1 > Sal2. % First employee salary greater than second
 
% Maintains a decreasing ordered list of employees truncated after (N) items.
% Rule 1: For N=0, always return an empty set.
% Rule 2: Add employee with greater salary at start of list, call with N-1
% Rule 3: Try to add new employee at N-1
% Rule 4: for an empty input list regardless of N, add the new employee
topSalary(0, _, _, []).
topSalary(N, Emp, [E|R], [Emp|Res]) :-
greater(Emp,E), N0 is N - 1, !, topSalary(N0, E, R, Res).
topSalary(N, Emp, [E|R], [E|Res]) :-
N0 is N - 1, !, topSalary(N0, Emp, R, Res).
topSalary(_, Emp, [], [Emp]).
 
% For each employee, add him to the list if top salary
topEmps(N, [Emp|Emps], R, Res) :-
topSalary(N, Emp, R, Rt), !, topEmps(N, Emps, Rt, Res).
topEmps(_, [], Res, Res).
 
% For each department, find the list of top employees in that department
topDeps(N, [Dept|T], [dept(Dept,Ro)|Res]) :-
findall(emp(Name, Id, Sal, Dept), emp(Name, Id, Sal, Dept), Emps),
topEmps(N, Emps, [], Ro), !, topDeps(N, T, Res).
topDeps(_, [], []).
 
% Calculate and report the list of highest salaried employees per department
topDeps(N) :-
departments(D), topDeps(N, D, Res),
member(dept(Dept,R), Res),
writef('Department: %w\n', [Dept]),
member(emp(Name,Id,Sal,_), R),
writef(' ID: %w\t%w\tSalary: %w\n', [Id,Name,Sal]),
fail.
topDeps(_).</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">?- topDeps(2).
Department: D101
ID: E00127 George Woltman Salary: 53500
ID: E04242 David McClellan Salary: 41500
Department: D050
ID: E21437 John Rappl Salary: 47000
ID: E41298 Nathan Adams Salary: 21900
Department: D202
ID: E01234 Rich Holcomb Salary: 49500
ID: E39876 Claire Buckman Salary: 27800
Department: D190
ID: E10001 Kim Arlich Salary: 57000
ID: E16398 Timothy Grove Salary: 29900
true.
</pre>
 
=={{header|PureBasic}}==
<langsyntaxhighlight PureBasiclang="purebasic">Structure Employees
Name$
ID$
Line 2,081 ⟶ 5,149:
NewList MyEmployees.Employees()
 
displayTopEarners(MyEmployees(), 3)</langsyntaxhighlight>
 
'''Save this as 'DataFile.txt and let the program open this file'''<pre>
Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
Line 2,096 ⟶ 5,164:
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190</pre>{{out}}
<pre style="height:30ex;overflow:scroll">Department D050
 
'''Ouput from the program looks like
<pre> Department D050
---------------
Name ID Salary
Line 2,132 ⟶ 5,198:
 
=={{header|Python}}==
Python 2.7/3.x compatible.
<lang python>from collections import defaultdict
<syntaxhighlight lang="python">from collections import defaultdict
from heapq import nlargest
 
def use_style(style, data): return ''.join(style % d for d in data)
 
def print_highest(n, data):
departments = defaultdict(list)
for rec in data[1:]:
departments[rec[-1]].append(rec)
 
style = " %-15s"
print '\n'.join("""
Department %s
%s
%s""" % (department,
use_style(style, data[0]),
'\n '.join(use_style(style, rec)
for rec in nlargest(n, recs, key=lambda rec: -rec[-2]))
# sorting by department gives nicer order
) for department, recs in sorted(departments.items()))
 
 
data = [('Employee Name', 'Employee ID', 'Salary', 'Department'),
('Tyler Bennett', 'E10297', 32000, 'D101'),
Line 2,168 ⟶ 5,216:
('Kim Arlich', 'E10001', 57000, 'D190'),
('Timothy Grove', 'E16398', 29900, 'D190')]
print_highest(3, data)
departments = defaultdict(list)
</lang>
for rec in data[1:]:
 
departments[rec[-1]].append(rec)
Output:<pre>
Department D050
N = 3
Employee Name Employee ID Salary Department
format = " %-15s " * len(data[0])
Nathan Adams E41298 21900 D050
for department, recs in sorted(departments.items()):
John Rappl E21437 47000 D050
print ("Department %s" % department)
print (format % data[0])
for rec in nlargest(N, recs, key=lambda rec: rec[-2]):
print (format % rec)
print('')</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department D050
Employee Name Employee ID Salary Department
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
 
Department D101
Employee Name Employee ID Salary Department
George Woltman Richard PotterE00127 E43128 53500 15900 D101
David McClellan TimE04242 Sampair E03033 41500 27000 D101 D101
Tyler Bennett E10297 32000 D101
 
Department D190
Employee Name Employee ID Salary Department
Kim Arlich Timothy Grove E16398 E10001 29900 57000 D190
Timothy Grove Kim Arlich E16398 E10001 29900 57000 D190 D190
 
Department D202
Employee Name Employee ID Salary Department
Rich Holcomb Adam Smith E01234 E63535 49500 18000 D202 D202
Claire Buckman David MotsingerE39876 E27002 1925027800 D202
David Motsinger ClaireE27002 Buckman E39876 19250 27800 D202
</pre>
 
Line 2,200 ⟶ 5,256:
 
Uses namedtuples for database records, and groupby builtin to group records by Department:
{{Works with|Python|3.7}}
<lang python>from collections import namedtuple
<syntaxhighlight lang="python">from collections import namedtuple
from itertools import groupby
 
Line 2,222 ⟶ 5,279:
rows = db.split('\n')
DBRecord = namedtuple('DBRecord', rows[0].replace(' ', '_'))
 
records = [ DBRecord(*row.split(',')) for row in rows[1:] ]
records = [DBRecord(*row.split(',')) for row in rows[1:]]
records.sort(key = lambda record: (record.Department, -float(record.Salary)))
 
print '\n\n'.join('\n '.join([dpt] + [str(g) for g in grp][:N])
records.sort(key=lambda record: (record.Department, -float(record.Salary)))
for dpt, grp in groupby(records,
 
lambda record: record.Department))</lang>
print('\n\n'.join(
Sample output
'\n '.join([dpt] + [str(g) for g in grp][:N])
<pre>D050
for dpt, grp in groupby(
records,
lambda record: record.Department
)
))</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">D050
DBRecord(Employee_Name='John Rappl', Employee_ID='E21437', Salary='47000', Department='D050')
DBRecord(Employee_Name='Nathan Adams', Employee_ID='E41298', Salary='21900', Department='D050')
Line 2,246 ⟶ 5,309:
=={{header|R}}==
First, read in the data.
<langsyntaxhighlight Rlang="r">dfr <- read.csv(tc <- textConnection(
"Employee Name,Employee ID,Salary,Department
Tyler Bennett,E10297,32000,D101
Line 2,260 ⟶ 5,323:
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190")); close(tc)</langsyntaxhighlight>
To just return the top salary, it's very simple using tapply.
<langsyntaxhighlight Rlang="r">with(dfr, tapply(Salary, Department, max))</langsyntaxhighlight>
To return N salaries, we replace max with our own function.
<langsyntaxhighlight Rlang="r">get.top.N.salaries <- function(N)
{
with(dfr, tapply(Salary, Department,
Line 2,276 ⟶ 5,339:
}
 
get.top.N.salaries(3)</langsyntaxhighlight>
$D050
[1] 47000 21900
Line 2,290 ⟶ 5,353:
 
To return the whole record for each of the top salaries, a different tack is required.
<langsyntaxhighlight Rlang="r">get.top.N.salaries2 <- function(N)
{
#Sort data frame by Department, then by Salary
Line 2,304 ⟶ 5,367:
})
}
get.top.N.salaries2(3)</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll"> $D050
$D050
Employee.Name Employee.ID Salary Department
2 John Rappl E21437 47000 D050
Line 2,325 ⟶ 5,388:
7 Rich Holcomb E01234 49500 D202
5 Claire Buckman E39876 27800 D202
10 David Motsinger E27002 19250 D202</pre>
 
=={{header|REXX}}= With dplyr ===
With the dplyr package we can use group_by and top_n to select the top n records in each group.
<lang rexx>/*REXX program shows top N salaries in each department (internal table).*/
parse arg topN . /*get number for top N salaries. */
if topN=='' then topN=1 /*if none, then assume only 1. */
say 'Finding top' topN 'salaries in each department.'; say
@.=
/*employee name, ID, salary, dept.*/
@.1="Tyler Bennett,E10297,32000,D101"
@.2="John Rappl,E21437,47000,D050"
@.3="George Woltman,E00127,53500,D101"
@.4="Adam Smith,E63535,18000,D202"
@.5="Claire Buckman,E39876,27800,D202"
@.6="David McClellan,E04242,41500,D101"
@.7="Rich Holcomb,E01234,49500,D202"
@.8="Nathan Adams,E41298,21900,D050"
@.9="Richard Potter,E43128,15900,D101"
@.10="David Motsinger,E27002,19250,D202"
@.11="Tim Sampair,E03033,27000,D101"
@.12="Kim Arlich,E10001,57000,D190"
@.13="Timothy Grove,E16398,29900,D190"
depts=
do j=1 until @.j=='' /*build database elements. */
parse var @.j name.j ',' id.j "," sal.j ',' dept.j
if wordpos(dept.j,depts)==0 then depts=depts dept.j
end /*j*/
employees=j-1
#d=words(depts)
say employees 'employees,' #d "departments:" depts; say
 
<syntaxhighlight lang="r">
do dep=1 for #d /*process each department. */
library(dplyr)
Xdept=word(depts,dep) /*current dept. being processed. */
dfr %>%
group_by(Department) %>%
top_n(2, Salary)
</syntaxhighlight>
Provides:
<pre style="height:30ex;overflow:scroll">
Employee.Name Employee.ID Salary Department
<fct> <fct> <int> <fct>
John Rappl E21437 47000 D050
George Woltman E00127 53500 D101
Claire Buckman E39876 27800 D202
David McClellan E04242 41500 D101
Rich Holcomb E01234 49500 D202
Nathan Adams E41298 21900 D050
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
</pre>
 
=={{header|Racket}}==
do topN /*process the top N salaries. */
<syntaxhighlight lang="racket">#lang racket
highsal=0 /*highest salary (so far). */
h=0 /*point to the highest paid emp. */
do e=1 for employees /*process each employee in Dept. */
if dept.e\==Xdept | sal.e<highsal then iterate
highsal=sal.e /*We found a higher salary. */
h=e /*Remember the high employee (H).*/
end /*e*/
 
(struct employee (name id salary dept))
if h==0 then iterate /*no highest paid this time? */
(define employees
say 'department: ' dept.h " $"sal.h id.h name.h /*show & tell.*/
(list (employee "Tyler Bennett" "E10297" 32000 "D101")
dept.h='' /*make sure we see employee again*/
(employee end"John Rappl" /*do topN*/ "E21437" 47000 "D050")
(employee "George Woltman" "E00127" 53500 "D101")
say /*write blank line between depts.*/
end (employee "Adam Smith" /*dep*/ "E63535" 18000 "D202")
(employee "Claire Buckman" "E39876" 27800 "D202")
/*stick a fork in it, we're done.*/</lang>
(employee "David McClellan" "E04242" 41500 "D101")
'''output''' when the input is: <tt> 2 </tt>
(employee "Rich Holcomb" "E01234" 49500 "D202")
(employee "Nathan Adams" "E41298" 21900 "D050")
(employee "Richard Potter" "E43128" 15900 "D101")
(employee "David Motsinger" "E27002" 19250 "D202")
(employee "Tim Sampair" "E03033" 27000 "D101")
(employee "Kim Arlich" "E10001" 57000 "D190")
(employee "Timothy Grove" "E16398" 29900 "D190")))
 
(define (top/dept N)
(for/list ([dept (remove-duplicates (map employee-dept employees))])
(define people
(filter (λ(e) (equal? dept (employee-dept e))) employees))
(cons dept (take (sort people > #:key employee-salary) N))))
 
(for ([dept (top/dept 2)])
(printf "Department ~a:\n" (car dept))
(for ([e (cdr dept)])
(printf " $~a: ~a (~a)\n"
(employee-salary e)
(employee-name e)
(employee-id e))))
</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department D101:
$53500: George Woltman (E00127)
$41500: David McClellan (E04242)
Department D050:
$47000: John Rappl (E21437)
$21900: Nathan Adams (E41298)
Department D202:
$49500: Rich Holcomb (E01234)
$27800: Claire Buckman (E39876)
Department D190:
$57000: Kim Arlich (E10001)
$29900: Timothy Grove (E16398)</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
We use whitespace-separated fields here from a heredoc; <tt>q:to/---/</tt> begins the heredoc. The <tt>Z=></tt> operator zips two lists into a list of pairs.
In <tt>MAIN</tt>, the <tt>classify</tt> method generates pairs where each key is a different department, and each value all the entries in that department. We then sort the pairs and process each department separately. Within each department, we sort on salary (negated to reverse the order). The last statement is essentially a list comprehension that uses a slice subscript with the <tt>^</tt> "up to" operator to take the first N elements of the sorted employee list. The <tt>:v</tt> modifier returns only valid values. The <tt>.<Name></tt> form is a slice hash subscript with literals strings. That in turn is just the subscript form of the <tt><...></tt> ("quote words") form, which is more familar to Perl 5 programmers as
<tt>qw/.../</tt>. We used that form earlier to label the initial data set.
 
This program also makes heavy use of method calls that start with dot. In Raku this means a method call on the current topic, <tt>$_</tt>, which is automatically set by any <tt>for</tt> or <tt>map</tt> construct that doesn't declare an explicit formal parameter on its closure.
 
<syntaxhighlight lang="raku" line>my @data = do for q:to/---/.lines -> $line {
E10297 32000 D101 Tyler Bennett
E21437 47000 D050 John Rappl
E00127 53500 D101 George Woltman
E63535 18000 D202 Adam Smith
E39876 27800 D202 Claire Buckman
E04242 41500 D101 David McClellan
E01234 49500 D202 Rich Holcomb
E41298 21900 D050 Nathan Adams
E43128 15900 D101 Richard Potter
E27002 19250 D202 David Motsinger
E03033 27000 D101 Tim Sampair
E10001 57000 D190 Kim Arlich
E16398 29900 D190 Timothy Grove
---
 
$%( < Id Salary Dept Name >
Z=>
$line.split(/ \s\s+ /)
)
}
 
sub MAIN(Int $N = 3) {
for @data.classify({ .<Dept> }).sort».value {
my @es = .sort: { -.<Salary> }
say '' if (state $bline)++;
say .< Dept Id Salary Name > for @es[^$N]:v;
}
}</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">
D050 E21437 47000 John Rappl
Finding top 2 salaries in each department.
D050 E41298 21900 Nathan Adams
 
D101 E00127 53500 George Woltman
13 employees, 4 departments: D101 D050 D202 D190
D101 E04242 41500 David McClellan
D101 E10297 32000 Tyler Bennett
 
D190 E10001 57000 Kim Arlich
department: D101 $53500 E00127 George Woltman
D190 E16398 29900 Timothy Grove
department: D101 $41500 E04242 David McClellan
 
D202 E01234 49500 Rich Holcomb
department: D050 $47000 E21437 John Rappl
D202 E39876 27800 Claire Buckman
department: D050 $21900 E41298 Nathan Adams
D202 E27002 19250 David Motsinger</pre>
 
=={{header|REXX}}==
department: D202 $49500 E01234 Rich Holcomb
===version 1===
department: D202 $27800 E39876 Claire Buckman
<syntaxhighlight lang="rexx">/*REXX program displays the top N salaries in each department (internal table). */
parse arg topN . /*get optional # for the top N salaries*/
if topN=='' | topN=="," then topN= 1 /*Not specified? Then use the default.*/
say 'Finding the top ' topN " salaries in each department."; say
@.= /*════════ employee name ID salary dept. ═══════ */
@.1 = "Tyler Bennett ,E10297, 32000, D101"
@.2 = "John Rappl ,E21437, 47000, D050"
@.3 = "George Woltman ,E00127, 53500, D101"
@.4 = "Adam Smith ,E63535, 18000, D202"
@.5 = "Claire Buckman ,E39876, 27800, D202"
@.6 = "David McClellan ,E04242, 41500, D101"
@.7 = "Rich Holcomb ,E01234, 49500, D202"
@.8 = "Nathan Adams ,E41298, 21900, D050"
@.9 = "Richard Potter ,E43128, 15900, D101"
@.10 = "David Motsinger ,E27002, 19250, D202"
@.11 = "Tim Sampair ,E03033, 27000, D101"
@.12 = "Kim Arlich ,E10001, 57000, D190"
@.13 = "Timothy Grove ,E16398, 29900, D190"
depts= /*build people database from @ array.*/
do j=1 until @.j==''; parse var @.j name.j ',' id.j "," sal.j ',' dept.j .
if wordpos(dept.j, depts)==0 then depts= depts dept.j /*a new DEPT?*/
end /*j*/
employees= j-1 /*adjust for the bumped DO loop index.*/
say 'There are ' employees "employees, " words(depts) 'departments: ' depts
say
do dep=1 for words(depts); say /*process each of the departments. */
Xdept= word(depts, dep) /*current department being processed. */
do topN; h= 0; highSal= 0 /*process the top N salaries. */
do e=1 for employees /*process each employee in department. */
if dept.e\==Xdept | sal.e<highSal then iterate /*is this the wrong info?*/
highSal= sal.e; h= e /*a higher salary was just discovered. */
end /*e*/
if h==0 then iterate /*do we have no highest paid this time?*/
say 'department: ' dept.h " $" || sal.h+0 id.h space(name.h)
dept.h= /*make sure we see the employee again. */
end /*topN*/
end /*dep*/ /*stick a fork in it, we're all done. */</syntaxhighlight>
{{out|output|text=&nbsp; when using the input: &nbsp; &nbsp; <tt> 2 </tt>}}
<pre>
Finding the top 2 salaries in each department.
 
There are 13 employees, 4 departments: D101 D050 D202 D190
department: D190 $57000 E10001 Kim Arlich
 
department: D190 $29900 E16398 Timothy Grove
 
department: D101 $53500 E00127 George Woltman
department: D101 $41500 E04242 David McClellan
 
department: D050 $47000 E21437 John Rappl
department: D050 $21900 E41298 Nathan Adams
 
department: D202 $49500 E01234 Rich Holcomb
department: D202 $27800 E39876 Claire Buckman
 
department: D190 $57000 E10001 Kim Arlich
department: D190 $29900 E16398 Timothy Grove
</pre>
'''output''' when the input is: <tt> 100 </tt>
<pre style="height:30ex;overflow:scroll">
Finding top 100 salaries in each department.
 
{{out|output|text=&nbsp; when using the input: &nbsp; &nbsp; <tt> 100 </tt>}}
13 employees, 4 departments: D101 D050 D202 D190
<pre>
Finding the top 100 salaries in each department.
 
There are 13 employees, 4 departments: D101 D050 D202 D190
 
 
department: D101 $53500 E00127 George Woltman
department: D101 $41500 E04242 David McClellan
department: D101 $32000 E10297 Tyler Bennett
department: D101 $27000 E03033 Tim Sampair
department: D101 $15900 E43128 Richard Potter
 
department: D050 $47000 E21437 John Rappl
department: D050 $21900 E41298 Nathan Adams
 
department: D202 $49500 E01234 Rich Holcomb
department: D202 $27800 E39876 Claire Buckman
department: D202 $19250 E27002 David Motsinger
department: D202 $18000 E63535 Adam Smith
 
department: D190 $57000 E10001 Kim Arlich
department: D190 $29900 E16398 Timothy Grove
</pre>
 
===version 2===
<syntaxhighlight lang="rexx">/* REXX ---------------------------------------------------------------
* 12.02.2014 Walter Pachl
*--------------------------------------------------------------------*/
Parse Arg topn . /* get number for top N salaries. */
Select
When topn='' Then /* no argument */
topn=1 /* assume only 1. */
When topn='?' Then /* user wants help */
Call help
When datatype(topn)<>'NUM' Then /* Argument is not a number */
Call exit 'Invalid argument ('topn'). Must be a number!'
Otherwise
Nop
End
Parse Value '0 0 0 0' with en dn esal. de. deptl
/*employee name, ID, salary, dept.*/
Call read "Tyler Bennett,E10297,32000,D101"
Call read "George Woltman,E00127,53500,D101"
Call read "John Rappl,E21437,47000,D050"
Call read "Adam Smith,E63535,18000,D202"
Call read "Claire Buckman,E39876,27800,D202"
Call read "David McClellan,E04242,41500,D101"
Call read "Rich Holcomb,E01234,49500,D202"
Call read "Nathan Adams,E41298,21900,D050"
Call read "Richard Potter,E43128,15900,D101"
Call read "David Motsinger,E27002,19250,D202"
Call read "Tim Sampair,E03033,27000,D101"
Call read "Kim Arlich,E10001,57000,D190"
Call read "Timothy Grove,E16398,29900,D190"
Say en 'employees,' dn "departments:" deptl
Do e=1 To en
d=dept.e
Do di=1 To de.d
If esal.d.di<sal.e Then
Leave
End
Do j=de.d To di By -1
j1=j+1
esal.d.j1=esal.d.j
enum.d.j1=enum.d.j
End
esal.d.di=sal.e
enum.d.di=id.e
de.d=de.d+1
End
/*---------------------------------------------------------------------
* Output
*--------------------------------------------------------------------*/
Say ' '
Say 'Showing top' topn 'salaries in each department.'
Say ' '
Do While deptl<>''
Parse Var deptl d deptl
Do i=1 To min(topn,de.d)
id=enum.d.i
Say 'department: 'd' $'esal.d.i id name.id
End
Say ' '
End
Exit
 
read:
en=en+1
Parse Arg name ',' id.en "," sal.en ',' dept.en
If wordpos(dept.en,deptl)=0 Then Do
dn=dn+1
deptl=deptl dept.en
End
z=id.en
name.z=name
Return
 
exit: Say arg(1)
help: Say 'Syntax: rexx topsal [topn]'
Exit</syntaxhighlight>
'''output'''
<pre>13 employees, 4 departments: D101 D050 D202 D190
 
Showing top 2 salaries in each department.
 
department: D101 $53500 E00127 George Woltman
department: D101 $41500 E04242 David McClellan
department: D101 $32000 E10297 Tyler Bennett
department: D101 $27000 E03033 Tim Sampair
department: D101 $15900 E43128 Richard Potter
 
department: D050 $47000 E21437 John Rappl
Line 2,410 ⟶ 5,684:
department: D202 $49500 E01234 Rich Holcomb
department: D202 $27800 E39876 Claire Buckman
department: D202 $19250 E27002 David Motsinger
department: D202 $18000 E63535 Adam Smith
 
department: D190 $57000 E10001 Kim Arlich
department: D190 $29900 E16398 Timothy Grove</pre>
 
=={{header|Ring}}==
<syntaxhighlight lang="ring">
# Project : Top rank per group
 
load "stdlib.ring"
salary = "Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190"
 
temp = substr(salary, ",", nl)
temp = str2list(temp)
depsal = newlist(13,4)
for n = 1 to len(temp)
n1 = ceil(n/4)
n2 = n%4
if n2 = 0
n2 = 4
ok
depsal[n1][n2] = temp[n]
next
for n = 1 to len(depsal)-1
for m = n+1 to len(depsal)
if strcmp(depsal[m][4], depsal[n][4]) < 0
tmp = depsal[n]
depsal[n] = depsal[m]
depsal[m] = tmp
ok
next
next
for n = 1 to len(depsal)-1
for m = n+1 to len(depsal)
if (depsal[m][4] = depsal[n][4]) and (depsal[m][3] > depsal[n][3])
tmp = depsal[n]
depsal[n] = depsal[m]
depsal[m] = tmp
ok
next
next
see "Department : " + depsal[1][4] + nl
see "Name " + "Id " + "Salary" + nl + nl
see "" + depsal[1][1] + " " + depsal[1][2] + " " + depsal[1][3]+ nl
for n = 1 to len(depsal)-1
if (depsal[n+1][4] != depsal[n][4])
see nl
see "Department : " + depsal[n+1][4] + nl
see "Name " + "Id " + "Salary" + nl + nl
see "" + depsal[n+1][1] + " " + depsal[n+1][2] + " " + depsal[n+1][3]+ nl
else
see "" + depsal[n+1][1] + " " + depsal[n+1][2] + " " + depsal[n+1][3]+ nl
ok
next
</syntaxhighlight>
Output:
<pre>
Department : D050
Name Id Salary
 
John Rappl E21437 47000
Nathan Adams E41298 21900
 
Department : D101
Name Id Salary
 
George Woltman E00127 53500
David McClellan E04242 41500
Tyler Bennett E10297 32000
Tim Sampair E03033 27000
Richard Potter E43128 15900
 
Department : D190
Name Id Salary
 
Kim Arlich E10001 57000
Timothy Grove E16398 29900
 
Department : D202
Name Id Salary
 
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
David Motsinger E27002 19250
Adam Smith E63535 18000
</pre>
 
=={{header|Ruby}}==
Without much thought to report formatting:
{{works with|Ruby|12.8.72+}}
<syntaxhighlight lang="ruby">require "csv"
<lang ruby>Employee = Struct.new(:name, :employee_id, :salary, :department)
 
data = <<EOS
def get_employees
Employee Name,Employee ID,Salary,Department
[
Employee.new("Tyler Bennett", "E10297", 32000, "D101"),
Employee.new("John Rappl", "E21437", 47000, "D050"),
Employee.new("George Woltman", "E00127", 53500, "D101"),
Employee.new("Adam Smith", "E63535", 18000, "D202"),
Employee.new("Claire Buckman", "E39876", 27800, "D202"),
Employee.new("David McClellan", "E04242", 41500, "D101"),
Employee.new("Rich Holcomb", "E01234", 49500, "D202"),
Employee.new("Nathan Adams", "E41298", 21900, "D050"),
Employee.new("Richard Potter", "E43128", 15900, "D101"),
Employee.new("David Motsinger", "E27002", 19250, "D202"),
Employee.new("Tim Sampair", "E03033", 27000, "D101"),
Employee.new("Kim Arlich", "E10001", 57000, "D190"),
Employee.new("Timothy Grove", "E16398", 29900, "D190"),
EOS
]
end
 
def show_top_salaries_per_group(groupsdata, n)
table = CSV.parse(data, :headers=>true, :header_converters=>:symbol)
groups = table.group_by{|emp| emp[:department]}.sort
groups.each do |dept, emps|
puts dept
# sortmax by salary descending
emps.sort_bymax_by(n) {|emp| -emp.[:salary}[0,n].to_i}.each {do |e| p e}
puts " %-16s %6s %7d" % [e[:employee_name], e[:employee_id], e[:salary]]
end
puts
end
end
 
show_top_salaries_per_group(data, 3)</syntaxhighlight>
groups = get_employees.group_by {|emp| emp.department}
 
{{out}}
show_top_salaries_per_group(groups,3)</lang>
<pre>
D050
John Rappl E21437 47000
Nathan Adams E41298 21900
 
D101
#<struct Employee name=" George Woltman", employee_id=" E00127", salary=53500, department="D101"> 53500
#<struct Employee name=" David McClellan", employee_id=" E04242", salary=41500, department="D101"> 41500
#<struct Employee name=" Tyler Bennett", employee_id=" E10297", salary=32000, department="D101"> 32000
 
D190
#<struct Employee name=" Kim Arlich", employee_id=" E10001", salary=57000, department="D190"> 57000
#<struct Employee name=" Timothy Grove", employee_id=" E16398", salary=29900, department="D190"> 29900
 
D202
#<struct Employee name=" Rich Holcomb", employee_id=" E01234", salary=49500, department="D202"> 49500
#<struct Employee name=" Claire Buckman", employee_id=" E39876", salary=27800, department="D202"> 27800
#<struct Employee name=" David Motsinger", employee_id=" E27002", salary=19250, department="D202"> 19250
</pre>
 
=={{header|Run BASIC}}==
<syntaxhighlight lang="runbasic">perSal$ = "Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050;
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202;
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190"
 
while word$(perSal$,n+1,chr$(13)) <> "" : n = n + 1 : wend ' get count of employees
dim depSal$(n)
for i = 1 to n
depSal$(i) = word$(perSal$,i,chr$(13))
next i
sw = 1
while sw = 1
sw = 0
for i = 1 to n -1
if word$(depSal$(i),4,",")+word$(depSal$(i),3,",") > word$(depSal$(i+1),4,",")+word$(depSal$(i+1),3,",") then
temp$ = depSal$(i)
depSal$(i) = depSal$(i+1)
depSal$(i+1) = temp$
sw = 1
end if
next i
wend
print "Employee Name";chr$(9);"ID";chr$(9);"Salary"
for i = 1 to n
if dep$ <> word$(depSal$(i),4,",") then
dep$ = word$(depSal$(i),4,",")
print : print"Department:";dep$
end if
print word$(depSal$(i),1,",");chr$(9);word$(depSal$(i),2,",");chr$(9);word$(depSal$(i),3,",")
next i</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Employee Name ID Salary
 
Department:D050
Nathan Adams E41298 21900
 
Department:D050;
John Rappl E21437 47000
 
Department:D101
Richard Potter E43128 15900
Tim Sampair E03033 27000
Tyler Bennett E10297 32000
David McClellan E04242 41500
George Woltman E00127 53500
 
Department:D190
Timothy Grove E16398 29900
Kim Arlich E10001 57000
 
Department:D202
David Motsinger E27002 19250
Claire Buckman E39876 27800
Rich Holcomb E01234 49500
 
Department:D202;
Adam Smith E63535 18000</pre>
 
=={{header|Rust}}==
The implementation counts with ties.
 
<syntaxhighlight lang="rust">#[derive(Debug)]
struct Employee<S> {
// Allow S to be any suitable string representation
id: S,
name: S,
department: S,
salary: u32,
}
 
impl<S> Employee<S> {
fn new(name: S, id: S, salary: u32, department: S) -> Self {
Self {
id,
name,
department,
salary,
}
}
}
 
#[rustfmt::skip]
fn load_data() -> Vec<Employee<&'static str>> {
vec![
Employee::new("Tyler Bennett", "E10297", 32000, "D101"),
Employee::new("John Rappl", "E21437", 47000, "D050"),
Employee::new("George Woltman", "E00127", 53500, "D101"),
Employee::new("Adam Smith", "E63535", 18000, "D202"),
Employee::new("Claire Buckman", "E39876", 27800, "D202"),
Employee::new("David McClellan", "E04242", 41500, "D101"),
Employee::new("Rich Holcomb", "E01234", 49500, "D202"),
Employee::new("Nathan Adams", "E41298", 21900, "D050"),
Employee::new("Richard Potter", "E43128", 15900, "D101"),
Employee::new("David Motsinger", "E27002", 19250, "D202"),
Employee::new("Tim Sampair", "E03033", 27000, "D101"),
Employee::new("Kim Arlich", "E10001", 57000, "D190"),
Employee::new("Timothy Grove", "E16398", 29900, "D190"),
// Added to demonstrate various tie situations
Employee::new("Kim Tie", "E16400", 57000, "D190"),
Employee::new("Timothy Tie", "E16401", 29900, "D190"),
Employee::new("Timothy Kim", "E16401", 19900, "D190"),
]
}
 
fn main() -> Result<(), Box<dyn std::error::Error>> {
let n = {
println!("How many top salaries to list? ");
let mut buf = String::new();
std::io::stdin().read_line(&mut buf)?;
buf.trim().parse::<u32>()?
};
 
let mut employees = load_data();
 
// Reverse order, then just pick top N employees
employees.sort_by(|a, b| b.salary.cmp(&a.salary));
 
let sorted = employees
.into_iter()
.fold(std::collections::BTreeMap::new(), |mut acc, next| {
// We store the number of unique salaries as well to handle
// ties (and list always all employees with the same salary)
let mut bucket = acc
.entry(next.department)
.or_insert_with(|| (0, Vec::<Employee<_>>::new()));
 
match bucket.1.last().map(|e| e.salary) {
Some(last_salary) if last_salary == next.salary => {
if bucket.0 <= n {
bucket.1.push(next);
}
}
 
_ => {
if bucket.0 < n {
bucket.0 += 1; // Next unique salary
bucket.1.push(next);
}
}
}
 
acc
});
 
for (department, (_, employees)) in sorted {
println!("{}", department);
 
employees
.iter()
.for_each(|employee| println!(" {:?}", employee));
}
 
Ok(())
}</syntaxhighlight>
 
{{out}}
<pre>How many top salaries to list?
3
D050
#<struct Employee { id: "E21437", name=: "John Rappl", employee_id=department: "E21437D050", salary=: 47000, department="D050">}
#<struct Employee { id: "E41298", name=: "Nathan Adams", employee_id=department: "E41298D050", salary=: 21900, department="D050">}
D101
Employee { id: "E00127", name: "George Woltman", department: "D101", salary: 53500 }
Employee { id: "E04242", name: "David McClellan", department: "D101", salary: 41500 }
Employee { id: "E10297", name: "Tyler Bennett", department: "D101", salary: 32000 }
D190
Employee { id: "E10001", name: "Kim Arlich", department: "D190", salary: 57000 }
Employee { id: "E16400", name: "Kim Tie", department: "D190", salary: 57000 }
Employee { id: "E16398", name: "Timothy Grove", department: "D190", salary: 29900 }
Employee { id: "E16401", name: "Timothy Tie", department: "D190", salary: 29900 }
Employee { id: "E16401", name: "Timothy Kim", department: "D190", salary: 19900 }
D202
Employee { id: "E01234", name: "Rich Holcomb", department: "D202", salary: 49500 }
Employee { id: "E39876", name: "Claire Buckman", department: "D202", salary: 27800 }
Employee { id: "E27002", name: "David Motsinger", department: "D202", salary: 19250 }
</pre>
 
=={{header|Scala}}==
===Application idiomatic version===
<lang scala>
<syntaxhighlight lang="scala">import scala.io.Source
object TopRank extends Application {
import scala.iolanguage._implicitConversions
import scala.language.reflectiveCalls
val data = """Tyler Bennett,E10297,32000,D101
import scala.collection.immutable.TreeMap
 
object TopRank extends App {
val topN = 3
 
val rawData = """Employee Name;Employee ID;Salary;Department
|Tyler Bennett;E10297;32000;D101
|John Rappl;E21437;47000;D050
|George Woltman;E00127;53500;D101
|Adam Smith;E63535;18000;D202
|Claire Buckman;E39876;27800;D202
|David McClellan;E04242;41500;D101
|Rich Holcomb;E01234;49500;D202
|Nathan Adams;E41298;21900;D050
|Richard Potter;E43128;15900;D101
|David Motsinger;E27002;19250;D202
|Tim Sampair;E03033;27000;D101
|Kim Arlich;E10001;57000;D190
|Timothy Grove;E16398;29900;D190""".stripMargin
 
class Employee(name: String, id: String,
val salary: Int,
val department: String) {
override def toString = s"$id\t$salary\t$name"
}
 
// A TreeMap has sorted keys
val data: TreeMap[String, Seq[TopRank.Employee]] = // TreeMap is a sorted map
TreeMap((Source.fromString(rawData) getLines ()).toSeq // Runtime parsing
.drop(1) // Drop header
.map(_.split(";")) //read fields into list of employees
.map(emp => new Employee(emp(0), emp(1), emp(2).toInt, emp(3)))
.groupBy(_.department).toSeq: _*)
 
implicit def iterableWithAvg[T: Numeric](data: Iterable[T]) = new {
def average[T](ts: Iterable[T])(implicit num: Numeric[T]) = {
num.toDouble(ts.sum) / ts.size
}
def avg = average(data)
}
 
val a = data.flatMap { case (_, emps) => emps.map(_.salary) }.avg
 
println(s"Reporting top $topN salaries in each department.\n")
 
println(s"Total of ${data.foldLeft(0)(_ + _._2.size)} employees in ${data.size} departments")
 
println(f"Average salary: $a%8.2f\n")
 
data.foreach {
case (dep, emps) => println(f"Department: $dep pop: ${emps.size} avg: ${emps.map(_.salary).avg}%8.2f\n"
+ emps.sortBy(-_.salary).take(topN)
.map(_.toString).mkString("\t", "\n\t", ""))
}
}</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Reporting top 3 salaries in each department.
 
Total of 13 employees in 4 departments
Average salary: 33865,38
 
Department: D190 pop: 2 avg: 43450,00
E10001 57000 Kim Arlich
E16398 29900 Timothy Grove
Department: D050 pop: 2 avg: 34450,00
E21437 47000 John Rappl
E41298 21900 Nathan Adams
Department: D101 pop: 5 avg: 33980,00
E00127 53500 George Woltman
E04242 41500 David McClellan
E10297 32000 Tyler Bennett
Department: D202 pop: 4 avg: 28637,50
E01234 49500 Rich Holcomb
E39876 27800 Claire Buckman
E27002 19250 David Motsinger
</pre>
===using SLICK ORM===
{{libheader|H2 Database Engine}} Version 1.3.1
<syntaxhighlight lang="scala">import scala.slick.driver.H2Driver.simple._
import scala.slick.lifted.ProvenShape
 
// A Employees table with 4 columns: Employee ID, Employee Name, Department, Salary
class Emp(tag: Tag) extends Table[(String, String, String, Double)](tag, "EMP") {
def id: Column[String] = column[String]("EMP_ID", O.PrimaryKey) // This is the primary key column
def name: Column[String] = column[String]("EMP_NAME", O.NotNull)
def deptId: Column[String] = column[String]("DEPT_ID", O.NotNull)
def salary: Column[Double] = column[Double]("SALARY", O.NotNull)
 
// Every table needs a * projection with the same type as the table's type parameter
def * : ProvenShape[(String, String, String, Double)] = (id, name, deptId, salary)
}
 
// The main application
object TopNrankSLICK extends App {
 
val topN = 3
 
// The query interface for the Emp table
val employees = TableQuery[Emp]
 
// Create a connection (called a "session") to an in-memory H2 database
Database.forURL("jdbc:h2:mem:hello", driver = "org.h2.Driver").withSession {
implicit session =>
 
// Create the schema
employees.ddl.create
 
// Fill the database
val employeesInsertResult: Option[Int] = employees ++= Seq(
("E10297", "Tyler Bennett", "D101", 32000),
("E21437", "John Rappl", "D050", 47000),
("E00127", "George Woltman", "D101", 53500),
("E63535", "Adam Smith", "D202", 18000),
("E39876", "Claire Buckman", "D202", 27800),
("E04242", "David McClellan", "D101", 41500),
("E01234", "Rich Holcomb", "D202", 49500),
("E41298", "Nathan Adams", "D050", 21900),
("E43128", "Richard Potter", "D101", 15900),
("E27002", "David Motsinger", "D202", 19250),
("E03033", "Tim Sampair", "D101", 27000),
("E10001", "Kim Arlich", "D190", 57000),
("E16398", "Timothy Grove", "D190", 29900),
("E16399", "Timothy Grave", "D190", 29900),
("E16400", "Timothy Grive", "D190", 29900))
 
/* Manual SQL / String Interpolation */
// Required import for the sql interpolator
import scala.slick.jdbc.StaticQuery.interpolation
 
// Construct a SQL statement manually with an interpolated value
val plainQuery = // First the bun - formatting SELECT clause
sql"""select case LINE
when 10 then
'Tot.' || LPAD(POPULATION, 2) || ' Employees in ' || TIE_COUNT ||
' deps.Avg salary:' || TO_CHAR(SALARY, '99990.99')
when 30 then
'-'
when 50 then
'Department: ' || DEPT_ID || ', pop: ' || POPULATION ||
'. Avg Salary: ' || TO_CHAR(SALARY, '99990.99')
when 70 then
LPAD('Employee ID', 14) || LPAD('Employee name', 20) ||
LPAD('Salary', 9) || 'Rank'
when 90 then
LPAD('+', 14, '-') || LPAD('+', 20, '-') || LPAD('+', 9, '-') ||
LPAD('+', 4, '-')
else
LPAD(' ', 8) || LPAD(EMP_ID, 6) || LPAD(EMP_NAME, 20) ||
TO_CHAR(SALARY, '99990.99') || LPAD(case when TIE_COUNT = 1 then ' ' else 'T' end || RANK, 4)
end "Top rank per group"
from (select 10 LINE
,null EMP_ID
,null EMP_NAME
,' ' DEPT_ID
,avg(SALARY) SALARY
,0 RANK
,count(distinct DEPT_ID) TIE_COUNT
,count(*) POPULATION
from EMP
union all
select 30 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 50 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,avg(SALARY) SALARY
,0 RANK
,0 TIE_COUNT
,count(*) POPULATION
from EMP
group by DEPT_ID
union all
select 70 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 90 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 110 LINE
,EMP_ID
,EMP_NAME
,DEPT_ID
,SALARY
,(select count(distinct EMP4.SALARY)
from EMP EMP4
where EMP4.DEPT_ID = EMP3.DEPT_ID
and EMP4.SALARY >= EMP3.SALARY) RANK
,(select count(*)
from EMP EMP2
where EMP2.DEPT_ID = EMP3.DEPT_ID
and EMP2.SALARY = EMP3.SALARY) TIE_COUNT
,0 POPULATION
from EMP EMP3
where $topN >= -- Here is the meat, Correlated subquery
(select count(distinct EMP4.SALARY)
from EMP EMP4
where EMP4.DEPT_ID = EMP3.DEPT_ID
and EMP4.SALARY >= EMP3.SALARY))
order by DEPT_ID ,LINE ,SALARY desc, EMP_ID""".as[String]
 
// Execute the query
plainQuery.foreach(println(_))
} // session
} // TopNrankSLICK</syntaxhighlight>
{{out}}
<pre style="height:30ex;overflow:scroll">Tot.15 Employees in 4 deps.Avg salary: 33336.67
-
Department: D050, pop: 2. Avg Salary: 34450.00
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E21437 John Rappl 47000.00 1
E41298 Nathan Adams 21900.00 2
-
Department: D101, pop: 5. Avg Salary: 33980.00
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E00127 George Woltman 53500.00 1
E04242 David McClellan 41500.00 2
E10297 Tyler Bennett 32000.00 3
-
Department: D190, pop: 4. Avg Salary: 36675.00
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E10001 Kim Arlich 57000.00 1
E16398 Timothy Grove 29900.00 T2
E16399 Timothy Grave 29900.00 T2
E16400 Timothy Grive 29900.00 T2
-
Department: D202, pop: 4. Avg Salary: 28637.50
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E01234 Rich Holcomb 49500.00 1
E39876 Claire Buckman 27800.00 2
E27002 David Motsinger 19250.00 3
</pre>
 
=={{header|Scheme}}==
{{works with|Gauche Scheme}}
<syntaxhighlight lang="scheme">(use gauche.record)
 
;; This will let us treat a list as though it is a structure (record).
(define-record-type (employee (pseudo-rtd <list>)) #t #t
name id salary dept)
 
(define (get-fields str)
(map (^x (if (#/^\d/ x) (string->number x) x))
(string-split str #\,)))
 
(define (print-record column-widths record)
(display " ")
(print (string-join
(map
(^(x width)
(if (number? x)
(format "~vD" width x)
(format "~vA" width x)))
record
column-widths))))
 
(define (get-column-widths records)
(apply
map
(lambda column
(apply max (map (compose string-length x->string) column)))
records))
 
(define records
(map get-fields
(string-split
"Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190"
#/\s*\n\s*/)))
 
(define (top-salaries n records)
(let ((departments (sort (delete-duplicates (map employee-dept records))))
(col-widths (get-column-widths records))
(sorted-by-salary (sort records > employee-salary)))
(dolist (dept departments)
(print dept)
(let1 matches (filter (^x (string=? dept (employee-dept x)))
sorted-by-salary)
(for-each
(pa$ print-record col-widths)
(take* matches n))))))</syntaxhighlight>
<b>Testing:</b>
<pre>
gosh> (top-salaries 0 records)
D050
D101
D190
D202
 
gosh> (top-salaries 2 records)
D050
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
D101
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
D190
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
D202
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
 
gosh> (top-salaries 999000 records)
D050
John Rappl E21437 47000 D050
Nathan Adams E41298 21900 D050
D101
George Woltman E00127 53500 D101
David McClellan E04242 41500 D101
Tyler Bennett E10297 32000 D101
Tim Sampair E03033 27000 D101
Richard Potter E43128 15900 D101
D190
Kim Arlich E10001 57000 D190
Timothy Grove E16398 29900 D190
D202
Rich Holcomb E01234 49500 D202
Claire Buckman E39876 27800 D202
David Motsinger E27002 19250 D202
Adam Smith E63535 18000 D202
</pre>
 
=={{header|Sidef}}==
{{trans|Perl}}
<syntaxhighlight lang="ruby">var data = <<'EOF'.lines.map{ <name id salary dept> ~Z .split(',') -> flat.to_h }
Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Line 2,488 ⟶ 6,408:
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190"""
EOF
 
var n = (ARGV ? Num(ARGV[0]) : "usage: #{__MAIN__} [n]\n".die)
class Employee(val name:String, val id:String, val salary:Int, val department:String) {
override def toString = id +"\t"+salary+"\t"+name
}
Source.fromString(data).getLines().map(_.split(",")).map(x => new Employee(x(0), x(1), x(2).toInt, x(3) )).toList // read data into list of employees
.sortBy(x => (x.department, -x.salary)).groupBy(_.department).foreach{ group => // sort and group by
println("Department: "+group._1)
group._2.take(3).foreach{x => println("\t"+x) }
}
}
</lang>
output:
<pre>Department: D050
E21437 47000 John Rappl
E41298 21900 Nathan Adams
Department: D101
E00127 53500 George Woltman
E04242 41500 David McClellan
E10297 32000 Tyler Bennett
Department: D190
E10001 57000 Kim Arlich
E16398 29900 Timothy Grove
Department: D202
E01234 49500 Rich Holcomb
E39876 27800 Claire Buckman
E27002 19250 David Motsinger</pre>
=={{header|SMEQL}}==
 
for d in (data.map {|h| h{:dept} }.uniq.sort) {
var es = data.grep { _{:dept} == d }.sort_by { -Num(_{:salary}) }
say d
n.times {
es || break
printf("%-15s | %-6s | %5d\n", es.shift(){<name id salary>...})
}
print "\n"
}</syntaxhighlight>
{{out}}
<pre>
$ sidef top_rank.sf 3
D050
John Rappl | E21437 | 47000
Nathan Adams | E41298 | 21900
 
D101
George Woltman | E00127 | 53500
David McClellan | E04242 | 41500
Tyler Bennett | E10297 | 32000
 
D190
Kim Arlich | E10001 | 57000
Timothy Grove | E16398 | 29900
 
D202
Rich Holcomb | E01234 | 49500
Claire Buckman | E39876 | 27800
David Motsinger | E27002 | 19250
</pre>
 
=={{header|SMEQL}}==
The following [[SMEQL]] example returns the top 6 earners in each department based on this table schema:
 
<langsyntaxhighlight lang="smeql">table: Employees
----------------
empID
dept
empName
salary</langsyntaxhighlight>
 
Source Code:
<syntaxhighlight lang="smeql">srt = orderBy(Employees, [dept, salary], order)
 
<lang smeql>srt = orderBy(Employees, [dept, salary], order)
top = group(srt, [(dept) dept2, max(order) order])
join(srt, top, a.dept=b.dept2 and b.order - a.order < 6)</langsyntaxhighlight>
 
Note: Since SMEQL is a query language, it has no built-in I/O capability to prompt the user for the threshold number ("6" in this example). However, it would be possible to retrieve the threshold from another table, but would make the example a bit confusing. In practice, an application language would most likely pass the value to it as a parameter.
 
=={{header|SQL}}==
<syntaxhighlight lang="sql">create table EMP
(
EMP_ID varchar2(6 char),
EMP_NAMEvarchar2(20 char),
DEPT_ID varchar2(4 char),
SALARY number(10,2)
);
 
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E21437','John Rappl','D050',47000);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E10297','Tyler Bennett','D101',32000);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E00127','George Woltman','D101',53500);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E63535','Adam Smith','D202',18000);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E39876','Claire Buckman','D202',27800);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E04242','David McClellan','D101',41500);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E41298','Nathan Adams','D050',21900);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E43128','Richard Potter','D101',15900);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E27002','David Motsinger','D202',19250);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E03033','Tim Sampair','D101',27000);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E10001','Kim Arlich','D190',57000);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E16398','Timothy Grove','D190',29900);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E01234','Rich Holcomb','D202',49500);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E16399','Timothy Grave','D190',29900);
insert into EMP (EMP_ID, EMP_NAME, DEPT_ID, SALARY)
values ('E16400','Timothy Grive','D190',29900);
COMMIT;</syntaxhighlight>
<syntaxhighlight lang="sql">select case LINE
when 10 then
'Tot.' || LPAD(POPULATION, 2) || ' Employees in ' || TIE_COUNT ||
' deps.Avg salary:' || TO_CHAR(SALARY, '99990.99')
when 30 then
'-'
when 50 then
'Department: ' || DEPT_ID || ', pop: ' || POPULATION ||
'. Avg Salary: ' || TO_CHAR(SALARY, '99990.99')
when 70 then
LPAD('Employee ID', 14) || LPAD('Employee name', 20) ||
LPAD('Salary', 9) || 'Rank'
when 90 then
LPAD('+', 14, '-') || LPAD('+', 20, '-') || LPAD('+', 9, '-') ||
LPAD('+', 4, '-')
else
LPAD(' ', 8) || LPAD(EMP_ID, 6) || LPAD(EMP_NAME, 20) ||
TO_CHAR(SALARY, '99990.99') || LPAD(case when TIE_COUNT = 1 then ' ' else 'T' end || RANK, 4)
end "Top rank per group"
from (select 10 LINE
,null EMP_ID
,null EMP_NAME
,' ' DEPT_ID
,avg(SALARY) SALARY
,0 RANK
,count(distinct DEPT_ID) TIE_COUNT
,count(*) POPULATION
from EMP
union all
select 30 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 50 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,avg(SALARY) SALARY
,0 RANK
,0 TIE_COUNT
,count(*) POPULATION
from EMP
group by DEPT_ID
union all
select 70 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 90 LINE
,null EMP_ID
,null EMP_NAME
,DEPT_ID
,0 SALARY
,0 RANK
,0 TIE_COUNT
,0 POPULATION
from EMP
group by DEPT_ID
union all
select 110 LINE
,EMP_ID
,EMP_NAME
,DEPT_ID
,SALARY
,(select count(distinct EMP4.SALARY)
from EMP EMP4
where EMP4.DEPT_ID = EMP3.DEPT_ID
and EMP4.SALARY >= EMP3.SALARY) RANK
,(select count(*)
from EMP EMP2
where EMP2.DEPT_ID = EMP3.DEPT_ID
and EMP2.SALARY = EMP3.SALARY) TIE_COUNT
,0 POPULATION
from EMP EMP3
where $topN >= -- Here is the meat, Correlated subquery
(select count(distinct EMP4.SALARY)
from EMP EMP4
where EMP4.DEPT_ID = EMP3.DEPT_ID
and EMP4.SALARY >= EMP3.SALARY))
order by DEPT_ID ,LINE ,SALARY desc, EMP_ID;</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Tot.15 employees in 4 deps.Avg salary: 33336.67
-
Department: D050, pop: 2. Avg salary: 34450.00
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E21437 John Rappl 47000.00 1
E41298 Nathan Adams 21900.00 2
-
Department: D101, pop: 5. Avg salary: 33980.00
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E00127 George Woltman 53500.00 1
E04242 David McClellan 41500.00 2
E10297 Tyler Bennett 32000.00 3
-
Department: D190, pop: 4. Avg salary: 36675.00
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E10001 Kim Arlich 57000.00 1
E16398 Timothy Grove 29900.00 T2
E16399 Timothy Grave 29900.00 T2
E16400 Timothy Grive 29900.00 T2
-
Department: D202, pop: 4. Avg salary: 28637.50
Employee ID Employee name SalaryRank
-------------+-------------------+--------+---+
E01234 Rich Holcomb 49500.00 1
E39876 Claire Buckman 27800.00 2
E27002 David Motsinger 19250.00 3</pre>
Note the T2 tie ranking aka ex aequo.
 
Since there is no requirement for the layout - using the data from the table above
<syntaxhighlight lang="sql">WITH ranked_emp AS (
SELECT emp_name
,dept_id
,salary
,DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary ) ranking
FROM emp
)
SELECT dept_id
,ranking
,emp_name
,salary
FROM ranked_emp
WHERE ranking <= 2
ORDER BY dept_id, ranking;</syntaxhighlight>
 
<pre style="height:30ex;overflow:scroll">
DEPT RANKING EMP_NAME SALARY
---- ---------- -------------------- ----------
D050 1 Nathan Adams 21900
D050 2 John Rappl 47000
D101 1 Richard Potter 15900
D101 2 Tim Sampair 27000
D190 1 Timothy Grove 29900
D190 1 Timothy Grave 29900
D190 1 Timothy Grive 29900
D190 2 Kim Arlich 57000
D202 1 Adam Smith 18000
D202 2 David Motsinger 19250</pre>
 
=={{header|Stata}}==
The following shows the top k salaries. If there are less than k employees in a department, this will print all salaries.
<syntaxhighlight lang="stata">import delimited employees.csv
local k 2
bysort department (salary): list salary if _N-_n<`k'</syntaxhighlight>
 
=={{header|Swift}}==
 
<syntaxhighlight lang="swift">struct Employee {
var name: String
var id: String
var salary: Int
var department: String
}
 
let employees = [
Employee(name: "Tyler Bennett", id: "E10297", salary: 32000, department: "D101"),
Employee(name: "John Rappl", id: "E21437", salary: 47000, department: "D050"),
Employee(name: "George Woltman", id: "E00127", salary: 53500, department: "D101"),
Employee(name: "Adam Smith", id: "E63535", salary: 18000, department: "D202"),
Employee(name: "Claire Buckman", id: "E39876", salary: 27800, department: "D202"),
Employee(name: "David McClellan", id: "E04242", salary: 41500, department: "D101"),
Employee(name: "Rich Holcomb", id: "E01234", salary: 49500, department: "D202"),
Employee(name: "Nathan Adams", id: "E41298", salary: 21900, department: "D050"),
Employee(name: "Richard Potter", id: "E43128", salary: 15900, department: "D101"),
Employee(name: "David Motsinger", id: "E27002", salary: 19250, department: "D202"),
Employee(name: "Tim Sampair", id: "E03033", salary: 27000, department: "D101"),
Employee(name: "Kim Arlich", id: "E10001", salary: 57000, department: "D190"),
Employee(name: "Timothy Grove", id: "E16398", salary: 29900, department: "D190")
]
 
func highestSalaries(employees: [Employee], n: Int = 1) -> [String: [Employee]] {
return employees.reduce(into: [:], {acc, employee in
guard var cur = acc[employee.department] else {
acc[employee.department] = [employee]
 
return
}
 
if cur.count < n {
cur.append(employee)
} else if cur.last!.salary < employee.salary {
cur[n - 1] = employee
}
 
acc[employee.department] = cur.sorted(by: { $0.salary > $1.salary })
})
}
 
for (dept, employees) in highestSalaries(employees: employees, n: 3) {
let employeeString = employees.map({ "\($0.name): \($0.salary)" }).joined(separator: "\n\t")
 
print("\(dept)'s highest paid employees are: \n\t\(employeeString)")
}</syntaxhighlight>
 
{{out}}
 
<pre style="height:30ex;overflow:scroll">D050's highest paid employees are:
John Rappl: 47000
Nathan Adams: 21900
D101's highest paid employees are:
George Woltman: 53500
David McClellan: 41500
Tyler Bennett: 32000
D202's highest paid employees are:
Rich Holcomb: 49500
Claire Buckman: 27800
David Motsinger: 19250
D190's highest paid employees are:
Kim Arlich: 57000
Timothy Grove: 29900</pre>
 
=={{header|Tcl}}==
{{works with|Tcl|8.5}}
<langsyntaxhighlight lang="tcl">package require Tcl 8.5
 
set text {Tyler Bennett,E10297,32000,D101
Line 2,569 ⟶ 6,762:
}
 
top_n_salaries 3 $data</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department D101
outputs
<pre>Department D101
George Woltman E00127 53500
David McClellan E04242 41500
Line 2,588 ⟶ 6,780:
Kim Arlich E10001 57000
Timothy Grove E16398 29900</pre>
 
 
 
=={{header|Transd}}==
Version using built-in database-like functionality.
 
<syntaxhighlight lang="scheme">#lang transd
 
 
MainModule: {
tbl : String(
`EmployeeName,EmployeeID,Salary:Int,Department
Tyler Bennett, E10297,32000,D101
John Rappl, E21437,47000,D050
George Woltman, E00127,53500,D101
Adam Smith, E63535,18000,D202
Claire Buckman, E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb, E01234,49500,D202
Nathan Adams, E41298,21900,D050
Richard Potter, E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair, E03033,27000,D101
Kim Arlich, E10001,57000,D190
Timothy Grove, E16398,29900,D190`),
 
N: 2,
_start: (λ
(with tabl Table()
(load-table tabl tbl)
(build-index tabl "Department")
(with rows (tsd-query tabl
select: ["Department"]
as: [[String()]] :distinct sortby: "Department" )
(for row in rows do
(with recs (tsd-query tabl
select: all
as: [[String(), String(), Int(), String()]]
satisfying: (lambda Department String()
(eq Department (get row 0)))
sortby: "Salary" :desc
limit: N)
(for rec in recs do (textout rec "\n")))))))
}</syntaxhighlight>{{out}}
<pre>
[D050, E21437, John Rappl, 47000]
[D050, E41298, Nathan Adams, 21900]
[D101, E00127, George Woltman, 53500]
[D101, E04242, David McClellan, 41500]
[D190, E10001, Kim Arlich, 57000]
[D190, E16398, Timothy Grove, 29900]
[D202, E01234, Rich Holcomb, 49500]
[D202, E39876, Claire Buckman, 27800]
</pre>
 
Version using untyped vectors.
<syntaxhighlight lang="scheme">
#lang transd
 
MainModule: {
tbl : String(
`"Tyler Bennett", "E10297",32000,"D101"
"John Rappl", "E21437",47000,"D050"
"George Woltman", "E00127",53500,"D101"
"Adam Smith", "E63535",18000,"D202"
"Claire Buckman", "E39876",27800,"D202"
"David McClellan", "E04242",41500,"D101"
"Rich Holcomb", "E01234",49500,"D202"
"Nathan Adams", "E41298",21900,"D050"
"Richard Potter", "E43128",15900,"D101"
"David Motsinger", "E27002",19250,"D202"
"Tim Sampair", "E03033",27000,"D101"
"Kim Arlich", "E10001",57000,"D190"
"Timothy Grove", "E16398",29900,"D190"`),
 
Record : typealias(Tuple<String String Int String>()),
N: 2,
curN: 0,
dep: String(),
deps: Index<String Int>(),
_start: (λ
(with recs Vector<Record>( untypedTable tbl "," "\n" )
res Vector<Record>()
(sort recs Range(-1 0)
(lambda l Record() r Record() -> Bool()
(ret (less (get l 2) (get r 2)))))
 
(for el in recs do
(= dep (get el 3))
(= curN (snd (get-s deps dep 0)))
(if (< curN N) (add res el) (set deps dep (+ curN 1)))
)
 
(sort res
(lambda l Record() r Record() -> Bool()
(ret (less (get l 3) (get r 3)))))
 
(for i in res do (textout i "\n"))
))
}
</syntaxhighlight>{{out}}
<pre>
[[John Rappl, E21437, 47000, D050]]
[[Nathan Adams, E41298, 21900, D050]]
[[George Woltman, E00127, 53500, D101]]
[[David McClellan, E04242, 41500, D101]]
[[Kim Arlich, E10001, 57000, D190]]
[[Timothy Grove, E16398, 29900, D190]]
[[Rich Holcomb, E01234, 49500, D202]]
[[Claire Buckman, E39876, 27800, D202]]
</pre>
 
=={{header|TUSCRIPT}}==
<langsyntaxhighlight lang="tuscript">$$ MODE TUSCRIPT
$$ MODE TUSCRIPT
MODE DATA
$$ SET dates=*
Line 2,637 ⟶ 6,939:
PRINT " "
ENDLOOP
ENDCOMPILE</syntaxhighlight>{{out}}
</lang>
Output:
<pre style='height:30ex;overflow:scroll'>
Department D050
Line 2,668 ⟶ 6,968:
Claire Buckman E39876 27800
David Motsinger E27002 19250
Adam Smith E63535 18000
</pre>
 
=={{header|TXR}}==
 
===Template Output Version===
 
This version massages the data in a way that is suitable for generating the output template-wise with an <code>@(output)</code> block.
 
The data is in a file, exactly as given in the problem. Parameter N is accepted from command line.
 
<syntaxhighlight lang="txr">@(next :args)
@{n-param}
@(next "top-rank-per-group.dat")
Employee Name,Employee ID,Salary,Department
@(collect :vars (record))
@name,@id,@salary,@dept
@(bind record (@(int-str salary) dept name id))
@(end)
@(bind (dept salary dept2 name id)
@(let* ((n (int-str n-param))
(dept-hash [group-by second record :equal-based])
(dept (hash-keys dept-hash))
(ranked (collect-each ((rec (hash-values dept-hash)))
[apply mapcar list [[sort rec > first] 0..n]])))
(cons dept [apply mapcar list ranked])))
@(output)
@ (repeat)
Department: @dept
@ (repeat)
@{name 15} (@id) $@{salary -6}
@ (end)
@ (end)
@(end)</syntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">Department: D101
George Woltman (E00127) $ 53500
David McClellan (E04242) $ 41500
Tyler Bennett (E10297) $ 32000
Department: D202
Rich Holcomb (E01234) $ 49500
Claire Buckman (E39876) $ 27800
David Motsinger (E27002) $ 19250
Department: D050
John Rappl (E21437) $ 47000
Nathan Adams (E41298) $ 21900
Department: D190
Kim Arlich (E10001) $ 57000
Timothy Grove (E16398) $ 29900</pre>
 
Breakdown:
 
Descend into argument list:
 
<syntaxhighlight lang="txr">@(next :args)</syntaxhighlight>
 
Collect first argument as <code>n-param</code> variable:
 
<syntaxhighlight lang="txr">@{n-param}</syntaxhighlight>
 
Drill into data file:
 
<syntaxhighlight lang="txr">@(next "top-rank-per-group.dat")</syntaxhighlight>
 
Match header exactly:
 
<syntaxhighlight lang="txr">Employee Name,Employee ID,Salary,Department</syntaxhighlight>
 
Now iterate over the data, requiring a variable called <code>record</code> to be bound in each iteration, and suppress all other variables from emerging. In the body of the collect, bind four variables. Then use these four variables to create a four-element list which is bound to the variable <code>record</code>. The <code>int-str</code> function converts the textual variable <code>salary</code> to an integer:
 
<syntaxhighlight lang="txr">@(collect :vars (record))
@name,@id,@salary,@dept
@(bind record (@(int-str salary) dept name id))
@(end)
</syntaxhighlight>
 
Next, we bind five variables to the output of some TXR Lisp code, which will return five lists:
 
<syntaxhighlight lang="txr">@(bind (dept salary dept2 name id)
@(let* ((n (int-str n-param))
(dept-hash [group-by second record :equal-based])
(dept (hash-keys dept-hash))
(ranked (collect-each ((rec (hash-values dept-hash)))
[apply mapcar list [[sort rec > first] 0..n]])))
(cons dept [apply mapcar list ranked])))</syntaxhighlight>
 
This code binds some successive variables. <code>n</code> is an integer conversion of the command line argument.
<code>dept-hash</code> is a hash whose keys are department strings, and whose values are lists of records belonging to each respective department (the records collected previously). The hash keys are the departments; these are extracted into a variable called <code>dept</code> for later use. The <code>ranked</code> variable takes the ranking information.
 
The salary ranking info is obtained by sorting each department's records by descending salary and then taking a 0..n slice of the list.
 
The "apply mapcar list" is a Lisp pattern for doing a matrix transpose. We use it twice: once within the department over the list of records, and then over the list of lists of records.
 
The reason for these transpositions is to convert the data into individual nested lists, once for each field. This is the format needed by the TXR <code>@(output)</code> clause:
 
<syntaxhighlight lang="txr">@(output)
@ (repeat)
Department: @dept
@ (repeat)
@{name 15} (@id) $@{salary -6}
@ (end)
@ (end)
@(end)</syntaxhighlight>
 
Here, all these variables are individual lists. The <code>dept</code> variable is a flat list; one nesting of <code>@(repeat)</code> iterates over it. The other variables are nested lists; a nested repeat drills into these.
 
===Lisp Output Version===
 
In this version, the Lisp processing block performs the output, so the conversion of records into lists for the template language is omitted, simplifying the code.
 
The output is identical to the previous version.
 
<syntaxhighlight lang="txr">@(next :args)
@{n-param}
@(next "top-rank-per-group.dat")
Employee Name,Employee ID,Salary,Department
@(collect :vars (record))
@name,@id,@salary,@dept
@(bind record (@(int-str salary) dept name id))
@(end)
@(do
(let* ((n (int-str n-param))
(dept-hash [group-by second record :equal-based])
(ranked (collect-each ((rec (hash-values dept-hash)))
[[sort rec > first] 0..n])))
(each ((d (hash-keys dept-hash))
(dept-recs ranked))
(put-line `Department: @d`)
(each ((r dept-recs))
(put-line ` @{r[2] 15} (@{r[3]}) $@{r[0] -6}`)))))</syntaxhighlight>
 
=={{header|Ursala}}==
Line 2,675 ⟶ 7,102:
sort each partition descending by the second to last, take the first
n strings in each partition, and display the list of them in a reasonably understandable form.
<langsyntaxhighlight Ursalalang="ursala">#import std
#import nat
 
Line 2,700 ⟶ 7,127:
#show+
 
main = top3 data</langsyntaxhighlight>{{out}}
<pre style="height:30ex;overflow:scroll">D190
output:
<pre>D190
Kim Arlich,E10001,57000
Timothy Grove,E16398,29900
Line 2,719 ⟶ 7,145:
John Rappl,E21437,47000
Nathan Adams,E41298,21900</pre>
 
=={{header|VBA}}==
Creating an Excel pivot table to display the top n salaried employees per department. Data is read from a file.
<syntaxhighlight lang="vb">Private Sub top_rank(filename As String, n As Integer)
Workbooks.OpenText filename:=filename, Comma:=True
Dim ws As Worksheet
Set ws = Sheets.Add: ws.Name = "output"
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _
"data!R1C1:R14C4", Version:=6).CreatePivotTable TableDestination:= _
"output!R3C1", TableName:="TableName", DefaultVersion:=6
With Sheets("output").PivotTables("TableName")
.InGridDropZones = True
.RowAxisLayout xlTabularRow
.AddDataField Sheets("output").PivotTables("TableName"). _
PivotFields("Salary"), "Top rank", xlSum
.PivotFields("Department").Orientation = xlRowField
.PivotFields("Department").Position = 1
.PivotFields("Salary").Orientation = xlRowField
.PivotFields("Salary").Position = 2
.PivotFields("Employee Name").Orientation = xlRowField
.PivotFields("Employee Name").Position = 3
.PivotFields("Employee ID").Orientation = xlRowField
.PivotFields("Employee ID").Position = 4
.PivotFields("Salary").PivotFilters.Add2 Type:=xlTopCount, _
DataField:=Sheets("output").PivotTables("TableName"). _
PivotFields("Top rank"), Value1:=n
.PivotFields("Salary").Subtotals = Array(False, False, False, False, _
False, False, False, False, False, False, False, False)
.PivotFields("Employee Name").Subtotals = Array(False, False, False, _
False, False, False, False, False, False, False, False, False)
.PivotFields("Department").Subtotals = Array(False, False, False, False, _
False, False, False, False, False, False, False, False)
.ColumnGrand = False
.PivotFields("Salary").AutoSort xlDescending, "Salary"
End With
End Sub
 
Public Sub main()
top_rank filename:="D:\data.txt", n:=3
End Sub</syntaxhighlight>
 
=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-sort}}
{{libheader|Wren-seq}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./dynamic" for Tuple
import "./sort" for Sort, Cmp
import "./seq" for Lst
import "./fmt" for Fmt
 
var Employee = Tuple.create("Employee", ["name", "id", "salary", "dept"])
 
var N = 2 // say
 
var employees = [
Employee.new("Tyler Bennett", "E10297", 32000, "D101"),
Employee.new("John Rappl", "E21437", 47000, "D050"),
Employee.new("George Woltman" , "E00127", 53500, "D101"),
Employee.new("Adam Smith", "E63535", 18000, "D202"),
Employee.new("Claire Buckman", "E39876", 27800, "D202"),
Employee.new("David McClellan", "E04242", 41500, "D101"),
Employee.new("Rich Holcomb", "E01234", 49500, "D202"),
Employee.new("Nathan Adams", "E41298", 21900, "D050"),
Employee.new("Richard Potter", "E43128", 15900, "D101"),
Employee.new("David Motsinger", "E27002", 19250, "D202"),
Employee.new("Tim Sampair", "E03033", 27000, "D101"),
Employee.new("Kim Arlich", "E10001", 57000, "D190"),
Employee.new("Timothy Grove", "E16398", 29900, "D190")
]
var cmpByDept = Fn.new { |employee1, employee2| Cmp.string.call(employee1.dept, employee2.dept) }
Sort.insertion(employees, cmpByDept)
var groupsByDept = Lst.groups(employees) { |e| e.dept }
System.print("Highest %(N) salaries by department:\n")
for (group in groupsByDept) {
var dept = group[0]
var groupEmployees = group[1].map { |i| i[0] }.toList
var cmpBySalary = Fn.new { |employee1, employee2| Cmp.numDesc.call(employee1.salary, employee2.salary) }
Sort.insertion(groupEmployees, cmpBySalary)
var topRanked = groupEmployees.take(N)
System.print("Dept %(dept) => ")
topRanked.each { |e| Fmt.print("$-15s $s $d", e.name, e.id, e.salary) }
System.print()
}</syntaxhighlight>
 
{{out}}
<pre>
Highest 2 salaries by department:
 
Dept D050 =>
John Rappl E21437 47000
Nathan Adams E41298 21900
 
Dept D101 =>
George Woltman E00127 53500
David McClellan E04242 41500
 
Dept D190 =>
Kim Arlich E10001 57000
Timothy Grove E16398 29900
 
Dept D202 =>
Rich Holcomb E01234 49500
Claire Buckman E39876 27800
</pre>
 
=={{header|XPL0}}==
<syntaxhighlight lang="xpl0">proc Sort(Array, Field, Size); \Sort Array in descending order by Field
int Array, Field, Size, I, J, T;
[for J:= Size-1 downto 0 do
for I:= 0 to J-1 do
if Array(I,Field) < Array(I+1,Field) then
[T:= Array(I); Array(I):= Array(I+1); Array(I+1):= T];
];
 
int Data, I, I0, Dept, N, Cnt;
[Data:=[["Tyler Bennett", "E10297", 32000, 101],
["John Rappl", "E21437", 47000, 050],
["George Woltman", "E00127", 53500, 101],
["Adam Smith", "E63535", 18000, 202],
["Claire Buckman", "E39876", 27800, 202],
["David McClellan", "E04242", 41500, 101],
["Rich Holcomb", "E01234", 49500, 202],
["Nathan Adams", "E41298", 21900, 050],
["Richard Potter", "E43128", 15900, 101],
["David Motsinger", "E27002", 19250, 202],
["Tim Sampair", "E03033", 27000, 101],
["Kim Arlich", "E10001", 57000, 190],
["Timothy Grove", "E16398", 29900, 190],
[0, 0, 0, 000]]; \sentinel
I:= 0; \find number of employees = Data size
while Data(I,0) # 0 do I:= I+1;
Sort(Data, 3, I); \sort by department field (3)
I:= 0; \sort by salary within each department
while Data(I,0) do
[Dept:= Data(I,3);
I0:= I;
repeat I:= I+1 until Data(I,3) # Dept;
Sort(@Data(I0), 2, I-I0);
];
N:= IntIn(0); \get parameter
I:= 0;
loop [Dept:= Data(I,3); \for each department
Text(0, "Department D");
if Dept < 100 then ChOut(0, ^0); IntOut(0, Dept); Text(0, ":^m^j");
Cnt:= 0;
loop [if Cnt >= N then
while Data(I,3) = Dept do I:= I+1; \skip any remaining
if Data(I,3) # Dept then quit;
IntOut(0, Data(I,2)); Text(0, " "); \salary
Text(0, Data(I,1)); Text(0, " "); \ID
Text(0, Data(I,0)); CrLf(0); \name
Cnt:= Cnt+1;
I:= I+1;
];
if Data(I,0) = 0 then quit;
];
]</syntaxhighlight>
 
{{out}}
<pre>
3
Department D202:
49500 E01234 Rich Holcomb
27800 E39876 Claire Buckman
19250 E27002 David Motsinger
Department D190:
57000 E10001 Kim Arlich
29900 E16398 Timothy Grove
Department D101:
53500 E00127 George Woltman
41500 E04242 David McClellan
32000 E10297 Tyler Bennett
Department D050:
47000 E21437 John Rappl
21900 E41298 Nathan Adams
</pre>
 
=={{header|zkl}}==
This reads a data file, the contents of which are as given by the task description (minus the header). To protect privacy, identifying information has been stripped from the output (aka laziness enhanced coding).
<syntaxhighlight lang="zkl">fcn setAppend(d,[(key,data)]){ d[key]=d.find(key,T).append(data) } //-->(key,(data,data...))
 
fcn topNsalaries(n){
File("data.txt").pump(setAppend.fp(data:=D()),fcn(line){ //-->Dictionary(dept:salaries)
line=line.strip().split(",");
T(line[-1],line[-2]); //-->(dept,salary)
});
dss:=data.pump(List,fcn([(dept,ss)],N){ //-->(dept,(salaries), dept...)
T(dept).append(ss.sort()[-N,*].reverse());
}.fp1(n)).sort(fcn(a,b){a[0]<b[0]});
foreach d,ss in (dss){
"%s: %s".fmt(d,ss.concat(",")).println();
}
}(3);</syntaxhighlight>
{{out}}
<pre>
D050: 47000,21900
D101: 53500,41500,32000
D190: 57000,29900
D202: 49500,27800,19250
</pre>
9,482

edits