Top rank per group
Find the top N salaries in each department, where N is provided as a parameter.
You are encouraged to solve this task according to the task description, using any language you may know.
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:
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
Ada
top.adb: <lang Ada>with Ada.Containers.Vectors; with Ada.Text_IO;
procedure Top is
type Departments is (D050, D101, D190, D202); type Employee_Data is record Name : String (1 .. 15); ID : String (1 .. 6); Salary : Positive; Department : Departments; end record;
package Employee_Vectors is new Ada.Containers.Vectors (Element_Type => Employee_Data, Index_Type => Positive);
function Compare_Salary (Left, Right : Employee_Data) return Boolean is begin return Left.Salary > Right.Salary; end Compare_Salary; package Salary_Sort is new Employee_Vectors.Generic_Sorting ("<" => Compare_Salary);
function Compare_Department (Left, Right : Employee_Data) return Boolean is begin return Left.Department < Right.Department; end Compare_Department; package Department_Sort is new Employee_Vectors.Generic_Sorting ("<" => Compare_Department);
Example_Data : Employee_Vectors.Vector;
begin
-- fill data Example_Data.Append (("Tyler Bennett ", "E10297", 32000, D101)); Example_Data.Append (("John Rappl ", "E21437", 47000, D050)); Example_Data.Append (("George Woltman ", "E00127", 53500, D101)); Example_Data.Append (("Adam Smith ", "E63535", 18000, D202)); Example_Data.Append (("Claire Buckman ", "E39876", 27800, D202)); Example_Data.Append (("David McClellan", "E04242", 41500, D101)); Example_Data.Append (("Rich Holcomb ", "E01234", 49500, D202)); Example_Data.Append (("Nathan Adams ", "E41298", 21900, D050)); Example_Data.Append (("Richard Potter ", "E43128", 15900, D101)); Example_Data.Append (("David Motsinger", "E27002", 19250, D202)); Example_Data.Append (("Tim Sampair ", "E03033", 27000, D101)); Example_Data.Append (("Kim Arlich ", "E10001", 57000, D190)); Example_Data.Append (("Timothy Grove ", "E16398", 29900, D190)); -- sort by salary Salary_Sort.Sort (Example_Data); -- output each department for Department in Departments loop declare Position : Employee_Vectors.Cursor := Example_Data.First; Employee : Employee_Data; begin Ada.Text_IO.Put_Line ("Department " & Departments'Image (Department)); for I in 1 .. 3 loop Employee := Employee_Vectors.Element (Position); while Employee.Department /= Department loop Position := Employee_Vectors.Next (Position); Employee := Employee_Vectors.Element (Position); end loop; Ada.Text_IO.Put_Line (" " & Employee.Name & " | " & Employee.ID & " | " & Positive'Image (Employee.Salary)); Position := Employee_Vectors.Next (Position); end loop; exception when Constraint_Error => null; end; end loop;
end Top;</lang>
- Output:
Department D050 John Rappl | E21437 | 47000 Nathan Adams | E41298 | 21900 Department D101 George Woltman | E00127 | 53500 David McClellan | E04242 | 41500 Tyler Bennett | E10297 | 32000 Department D190 Kim Arlich | E10001 | 57000 Timothy Grove | E16398 | 29900 Department D202 Rich Holcomb | E01234 | 49500 Claire Buckman | E39876 | 27800 David Motsinger | E27002 | 19250
Aime
<lang aime>void Add_Employee(record employees, text name, text id, integer salary, text department) {
l_bill(r_n_list(employees, name), -1, name, id, salary, department);
}
void collect(record top, record employees) {
text name;
r_first(employees, name); do {
list department, l;
l_set(l, r_q_list(employees, name));
if (r_key(top, l_q_text(l, 3))) { integer i, salary;
l_set(department, r_q_list(top, l_q_text(l, 3)));
salary = l_q_integer(l, 2); i = 0; while (i < l_length(department)) { if (l_q_integer(l_q_list(department, i), 2) < salary) { break; } i += 1; }
l_l_list(department, i, l);
if (l_length(department) == N + 1) { l_delete(department, N); } } else { l_set(department, r_n_list(top, l_q_text(l, 3))); l_l_list(department, 0, l); }
} while (r_greater(employees, name, name));
}
void print(record top) {
text department;
r_first(top, department); do {
list employees; integer i;
o_plan("Department ", department, "\n");
l_set(employees, r_q_list(top, department)); i = 0; while (i < l_length(employees)) { list l;
l_set(l, l_q_list(employees, i));
o_form(" ~ | ~ | ~\n", l_q_text(l, 0), l_q_text(l, 1), l_q_integer(l, 2));
i += 1; }
} while (r_greater(top, department, department));
}
integer 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);
print(top);
return 0;
}</lang>Run as:
aime rcs/top_rank_per_group c N 5
- Output:
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
AutoHotkey
<lang autohotkey>Departments = D050, D101, D190, D202 StringSplit, Department_, Departments, `,, %A_Space%
- Employee Name, Employee ID, Salary, Department
Add_Employee("Tyler Bennett ", "E10297", 32000, "D101") Add_Employee("John Rappl ", "E21437", 47000, "D050") Add_Employee("George Woltman ", "E00127", 53500, "D101") Add_Employee("Adam Smith ", "E63535", 18000, "D202") Add_Employee("Claire Buckman ", "E39876", 27800, "D202") Add_Employee("David McClellan", "E04242", 41500, "D101") Add_Employee("Rich Holcomb ", "E01234", 49500, "D202") Add_Employee("Nathan Adams ", "E41298", 21900, "D050") Add_Employee("Richard Potter ", "E43128", 15900, "D101") Add_Employee("David Motsinger", "E27002", 19250, "D202") Add_Employee("Tim Sampair ", "E03033", 27000, "D101") Add_Employee("Kim Arlich ", "E10001", 57000, "D190") Add_Employee("Timothy Grove ", "E16398", 29900, "D190")
- display top 3 ranks for each department
Loop, %Department_0% ; all departments
MsgBox,, % "Department: " Department_%A_Index% , % TopRank(3, Department_%A_Index%)
- ---------------------------------------------------------------------------
TopRank(N, Department) { ; find the top N salaries in each department
- ---------------------------------------------------------------------------
local Collection := Msg := "" Loop, %m% ; all employees If (Employee_%A_Index%_Dept = Department) ; collect all the salaries being paid in this department Collection .= (Collection ? "," : "") Employee_%A_Index%_Salary Sort, Collection, ND,R StringSplit, Collection, Collection, `, Loop, % (N < Collection0) ? N : Collection0 { Salary := Collection%A_Index% Loop, %m% ; find the respective employee If (Employee_%A_Index%_Salary = Salary) ; and put out his/her details Msg .= Employee_%A_Index%_Name "`t" . Employee_%A_Index%_ID "`t" . Employee_%A_Index%_Salary "`t" . Employee_%A_Index%_Dept "`t`n" } Return, Msg
}
- ---------------------------------------------------------------------------
Add_Employee(Name, ID, Salary, Department) {
- ---------------------------------------------------------------------------
global m++ Employee_%m%_Name := Name Employee_%m%_ID := ID Employee_%m%_Salary := Salary Employee_%m%_Dept := Department
}</lang>
- Output:
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
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: a^b*a^c
→ a^(b+c)
. This built-in behaviour is made use of in this solution.
<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)
& ;</lang>
- Output:
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
C
<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; }
- define N sizeof(ppl)/sizeof(person)
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;
}</lang>
- Output:
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
C++
<lang cpp>#include <string>
- include <set>
- include <list>
- include <map>
- include <iostream>
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; } };
// We'll tell std::set to use this to sort our employees. struct CompareEarners { bool operator()(const Employee& e1, const Employee& e2) { return (e1.Salary > e2.Salary); } };
// A few typedefs to make the code easier to type, read and maintain. typedef std::list<Employee> EMPLOYEELIST;
// Notice the CompareEarners; We're telling std::set to user our specified comparison mechanism // to sort its contents. typedef std::set<Employee, CompareEarners> DEPARTMENTPAYROLL;
typedef std::map<std::string, DEPARTMENTPAYROLL> DEPARTMENTLIST;
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;
}</lang>
- Output:
In department D050 Name ID Salary Department John Rappl E21437 47000 D050 Nathan Adams E41298 21900 D050 In department D101 Name ID Salary Department George Woltman E21437 53500 D101 David McClellan E04242 41500 D101 Tyler Bennett E10297 32000 D101 In department D190 Name ID Salary Department Kim Arlich E10001 57000 D190 Timothy Grove E16398 29900 D190 In department D202 Name ID Salary Department Rich Holcomb E01234 49500 D202 Claire Buckman E39876 27800 D202 David Motsinger E27002 19250 D202
C#
<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("----------------------------"); } }
}</lang>
- Output:
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 ----------------------------
Online demo: http://ideone.com/95TxAV
Clojure
<lang lisp>(use '[clojure.contrib.seq-utils :only (group-by)])
(defstruct employee :Name :ID :Salary :Department)
(def 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)) (map #(apply (partial struct employee) %))))
(doseq [[dep emps] (group-by :Department data)]
(println "Department:" dep) (doseq [e (take 3 (reverse (sort-by :Salary emps)))] (println e)))
</lang>
- Output:
Department: D050 {:Name John Rappl, :ID E21437, :Salary 47000, :Department D050} {:Name Nathan Adams, :ID E41298, :Salary 21900, :Department D050} Department: D101 {:Name George Woltman, :ID E00127, :Salary 53500, :Department D101} {:Name David McClellan, :ID E04242, :Salary 41500, :Department D101} {:Name Tyler Bennett, :ID E10297, :Salary 32000, :Department D101} Department: D190 {:Name Kim Arlich, :ID E10001, :Salary 57000, :Department D190} {:Name Timothy Grove, :ID E16398, :Salary 29900, :Department D190} Department: D202 {:Name Rich Holcomb, :ID E01234, :Salary 49500, :Department D202} {:Name Claire Buckman, :ID E39876, :Salary 27800, :Department D202} {:Name David Motsinger, :ID E27002, :Salary 19250, :Department D202}
Common Lisp
<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))) (labels ((value (datum) (funcall value-key datum)) (insert (x list) (merge 'list (list x) list not-pred :key #'value)) (entry (group) "Return the entry for the group, creating it if necessary. An entry is a list whose first element is k, the number of items currently associated with the group (out of n total), and whose second element is the list of the k current top items for the group." (multiple-value-bind (entry presentp) (gethash group group-data) (if presentp entry (setf (gethash group group-data) (list 0 '()))))) (update-entry (entry datum) "Update the entry using datum. If there are already n items associated with the entry, then when datum's value is greater than the current least item, data is merged into the items, and the list (minus the first element) is stored in entry. Otherwise, if there are fewer than n items in the entry, datum is merged in, and the entry's k is increased by 1." (if (= n (first entry)) (when (funcall predicate (value datum) (value (first (second entry)))) (setf (second entry) (cdr (insert datum (second entry))))) (setf (first entry) (1+ (first entry)) (second entry) (insert datum (second entry)))))) (dolist (datum data group-data) (update-entry (entry (funcall group-key datum)) datum)))))</lang>
Example <lang lisp>> (defparameter *employee-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)) "A list of lists of each employee's name, id, salary, and
department.")
- EMPLOYEE-DATA*
> (top-n-by-group 3 *employee-data* 'third 'fourth '>)
- <EQL Hash Table{4} 2361A0E7>
> (describe *)
- <EQL Hash Table{4} 2361A0E7> is a HASH-TABLE
D101 (3 (("Tyler Bennett" E10297 32000 D101) ("David McClellan" E04242 41500 D101) ("George Woltman" E00127 53500 D101))) 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)))</lang>
D
<lang d>import std.stdio, std.algorithm, std.conv, std.range;
struct Employee {
string name, id; uint salary; string department;
}
immutable Employee[] data = [
{"Tyler Bennett", "E10297", 32_000, "D101"}, {"John Rappl", "E21437", 47_000, "D050"}, {"George Woltman", "E00127", 53_500, "D101"}, {"Adam Smith", "E63535", 18_000, "D202"}, {"Claire Buckman", "E39876", 27_800, "D202"}, {"David McClellan", "E04242", 41_500, "D101"}, {"Rich Holcomb", "E01234", 49_500, "D202"}, {"Nathan Adams", "E41298", 21_900, "D050"}, {"Richard Potter", "E43128", 15_900, "D101"}, {"David Motsinger", "E27002", 19_250, "D202"}, {"Tim Sampair", "E03033", 27_000, "D101"}, {"Kim Arlich", "E10001", 57_000, "D190"}, {"Timothy Grove", "E16398", 29_900, "D190"}];
void main(in string[] args) {
immutable n = (args.length == 2) ? to!int(args[1]) : 3;
Employee[][string] departments; foreach (immutable rec; data) departments[rec.department] ~= rec;
foreach (dep, recs; departments) { recs.topN!q{a.salary > b.salary}(n); writefln("Department %s\n %(%s\n %)\n", dep, recs.take(n)); }
}</lang>
- Output:
Department D202 Employee("Rich Holcomb", "E01234", 49500, "D202") Employee("Claire Buckman", "E39876", 27800, "D202") Employee("David Motsinger", "E27002", 19250, "D202") Department D190 Employee("Kim Arlich", "E10001", 57000, "D190") Employee("Timothy Grove", "E16398", 29900, "D190") Department D101 Employee("George Woltman", "E00127", 53500, "D101") Employee("David McClellan", "E04242", 41500, "D101") Employee("Tyler Bennett", "E10297", 32000, "D101") Department D050 Employee("John Rappl", "E21437", 47000, "D050") Employee("Nathan Adams", "E41298", 21900, "D050")
E
<lang e>/** Turn a list of arrays into a list of maps with the given keys. */ def addKeys(keys, rows) {
def res := [].diverge() for row in rows { res.push(__makeMap.fromColumns(keys, row)) } return res.snapshot()
}
def data := addKeys(
["name", "id", "salary", "dept"], [["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"]])
def topSalaries(n, out) {
var groups := [].asMap() for row in data { def [=> salary, => dept] | _ := row def top := groups.fetch(dept, fn {[]}).with([-salary, row]).sort() groups with= (dept, top.run(0, top.size().min(n))) } for dept => group in groups.sortKeys() { out.println(`Department $dept`) out.println(`---------------`) for [_, row] in group { out.println(`${row["id"]} $$${row["salary"]} ${row["name"]}`) } out.println() }
}</lang>
Note: This uses an append-and-then-sort to maintain the list of top N; a sorted insert or a proper selection algorithm would be more efficient. As long as N is small, this does not matter much; the algorithm is O(n) with respect to the data set.
- Output:
? topSalaries(3, stdout) 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
Erlang
<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().
</lang>
- Output:
<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"
F#
<lang fsharp>let 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"; ]
let topRank n =
Seq.groupBy (fun (_, _, _, d) -> d) data |> Seq.map (snd >> Seq.sortBy (fun (_, _, s, _) -> -s) >> Seq.take n)</lang>
Factor
<lang factor>USING: accessors assocs fry io kernel math.parser sequences sorting ; IN: top-rank
TUPLE: employee name id salary department ;
CONSTANT: employees {
T{ employee f "Tyler Bennett" "E10297" 32000 "D101" } T{ employee f "John Rappl" "E21437" 47000 "D050" } T{ employee f "George Woltman" "E00127" 53500 "D101" } T{ employee f "Adam Smith" "E63535" 18000 "D202" } T{ employee f "Claire Buckman" "E39876" 27800 "D202" } T{ employee f "David McClellan" "E04242" 41500 "D101" } T{ employee f "Rich Holcomb" "E01234" 49500 "D202" } T{ employee f "Nathan Adams" "E41298" 21900 "D050" } T{ employee f "Richard Potter" "E43128" 15900 "D101" } T{ employee f "David Motsinger" "E27002" 19250 "D202" } T{ employee f "Tim Sampair" "E03033" 27000 "D101" } T{ employee f "Kim Arlich" "E10001" 57000 "D190" } T{ employee f "Timothy Grove" "E16398" 29900 "D190" } }
- group-by ( seq quot -- hash )
H{ } clone [ '[ dup @ _ push-at ] each ] keep ; inline
- prepare-departments ( seq -- departments )
[ department>> ] group-by [ [ salary>> ] inv-sort-with ] assoc-map ;
- first-n-each ( seq n quot -- )
[ short head-slice ] dip each ; inline
- main ( -- )
employees prepare-departments [ [ "Department " write write ":" print ] dip 3 [ [ id>> write " $" write ] [ salary>> number>string write " " write ] [ name>> print ] tri ] first-n-each nl
] assoc-each ;</lang>
- Output:
<pre> Department D101: E00127 $53500 George Woltman E04242 $41500 David McClellan E10297 $32000 Tyler Bennett Department D202: E01234 $49500 Rich Holcomb E39876 $27800 Claire Buckman E27002 $19250 David Motsinger Department D190: E10001 $57000 Kim Arlich E16398 $29900 Timothy Grove Department D050: E21437 $47000 John Rappl E41298 $21900 Nathan Adams
FunL
<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()</lang>
- Output:
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
Go
Handling the possibility of ties: <lang go>package main
import (
"fmt" "sort"
)
// language-native data description type employee struct {
name, id string salary int dept string
}
var data = []employee{
{"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 provided as first parameter of function topN func main() {
topN(3, data).print()
}
// return type of function topN type result struct {
n int dlist [][][]int // dimensions are department,salary,index into data
}
func (r result) print() {
fmt.Println("Top", r.n, "salaries per department") printEs := func(es []int) { for _, ei := range es { fmt.Printf(" %s %16s %7d\n", data[ei].id, data[ei].name, data[ei].salary) } } for _, slist := range r.dlist { fmt.Println(data[slist[0][0]].dept) nRemaining := r.n sLast := len(slist) - 1 for si := 0; si < sLast; si++ { printEs(slist[si]) nRemaining -= len(slist[si]) } if len(slist[sLast]) > nRemaining { fmt.Println(" * * * * * * * tie * * * * * * *") } printEs(slist[sLast]) }
}
func topN(n int, data []employee) *result {
// summarize: construct map with unique deptartment names, // unique salaries under each of those, // and then a list of employees under each of those, // with employees identified by index into data. salsum := make(map[string]map[int][]int) for i, e := range data { dsum, ok := salsum[e.dept] if !ok { dsum = make(map[int][]int) salsum[e.dept] = dsum } dsum[e.salary] = append(dsum[e.salary], i) }
// begin constructing result array. // top level is list of departments. dlist := make([][][]int, len(salsum))
// sort departments for readability deptSort := make([]string, len(salsum)) i := 0 for dept := range salsum { deptSort[i] = dept i++ } sort.Strings(deptSort)
for di, dept := range deptSort { dsum := salsum[dept] // 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 } return &result{n, dlist}
}</lang>
- Output:
Top 3 salaries per department D050 E21437 John Rappl 47000 E41298 Nathan Adams 21900 D101 E00127 George Woltman 53500 E04242 David McClellan 41500 E10297 Tyler Bennett 32000 D190 E10001 Kim Arlich 57000 E16398 Timothy Grove 29900 D202 E01234 Rich Holcomb 49500 E39876 Claire Buckman 27800 E27002 David Motsinger 19250
- Output with additional data demonstrating ties:
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 E39876 Claire Buckman 27800 E27002 David Motsinger 19250
Groovy
<lang groovy>def displayRank(employees, number) {
employees.groupBy { it.Department }.sort().each { department, staff -> println "Department $department" println " Name ID Salary" staff.sort { e1, e2 -> e2.Salary <=> e1.Salary } staff[0..<Math.min(number, staff.size())].each { e -> println " ${e.Name.padRight(20)}${e.ID}${sprintf('%8d', e.Salary)}" } println() }
}
def employees = [
[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']
]
displayRank(employees, 3)</lang>
- Output:
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 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
Haskell
<lang haskell>import Data.List import Control.Monad import Control.Arrow import Text.Printf import Data.Ord import Data.Function
groupingOn = ((==) `on`) comparingDwn = flip . comparing
type ID = Int
type DEP = String
type NAME = String
type SALARY = Double
data Employee = Employee {nr :: ID, dep :: DEP, name :: NAME, sal :: SALARY}
employees :: [Employee] employees = map (\(i,d,n,s) -> Employee i d n s)
[(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) ]
dorank :: Int ->
(Employee -> DEP) -> (Employee -> SALARY) -> [Employee]-> Employee
dorank n o1 o2 = map (take n. sortBy (comparingDwn o2))
. groupBy (groupingOn o1) . sortBy (comparing o1)
toprank :: IO () toprank = do
printf "%-16s %3s %10s\n" "NAME" "DEP" "TIP" putStrLn $ replicate 31 '='
mapM_ (mapM_ (ap (ap (printf "%-16s %3s %10.2g\n" . name) dep) sal)) $ dorank 3 dep sal employees</lang>
- Output:
*Main> toprank NAME DEP TIP =============================== de Bont C.A. AB 65000.00 Anders H. AB 50000.00 de Slegte S. AB 46987.00 Uitlaat G.A.S. CC 44500.00 de Goeij J. CC 39000.00 Zagt A. CC 33000.00 Telmans R.M. CD 55500.00 Janszen H.P. CD 41000.00 Janszoon A. CD 38665.00 Ezelbips P.J. KA 52000.00 't Woud B. KA 45000.00 Pragtweik J.M.V. KA 42300.00
HicEst
<lang 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
! Open the source with 4 "," separated columns, skip line 1: OPEN(FIle=source, Format='SL=1;4,;', LENgth=L) ALLOCATE(idx, L)
! Sort salary column 3 descending, then department column 4, store in idx: SORT(FIle=source, Column=3, Descending=1, Column=4, Index=idx)
! Display a spreadsheet-like scrolling dialog of the presorted source: DLG(Text=idx, Text=source, Format=fmt, Y)
! Output the first N top salaries of each department_ OPEN(FIle=outP) DO i = 1, L
rank = rank + 1 READ(FIle=source, Row=idx(i)) name, employee_ID, salary, department IF(temp /= department) THEN rank = 1 WRITE(FIle=outP) temp = department ENDIF IF(rank <= N_top_salaries) THEN WRITE(FIle=outP, Format=fmt) name, employee_ID, salary, department ENDIF
ENDDO
END</lang>
- Output:
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
Note: In place of writing the sorted result to file outP, DLG allows the direct export of the formatted and sorted result. Use of the CLUSter= and Groups= options of SORT performs a variance controlled cluster sort of up to 8 columns instead of the department step sort in this example.
Icon and Unicon
<lang icon>record Employee(name,id,salary,dept)
procedure getEmployees ()
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") ] return employees
end
procedure show_employee (employee)
every writes (!employee || " ") write ()
end
procedure main (args)
N := integer(args[1]) # N set from command line employees := getEmployees () groups := set() every employee := !employees do insert(groups, employee.dept)
every group := !groups do { write ("== Top " || N || " in group " || group) employeesInGroup := [] every employee := !employees do { if employee.dept == group then put(employeesInGroup, employee) } # sort by third field and reverse, so highest salary comes first employeesInGroup := reverse(sortf(employeesInGroup, 3)) # show the first N records, up to the end of the list every show_employee (!employeesInGroup[1:(1+min(N,*employeesInGroup))]) }
end</lang>
- Output:
$ ./top-rank-by-group 2 == Top 2 in group D101 George Woltman E00127 53500 D101 David McClellan E04242 41500 D101 == Top 2 in group D202 Rich Holcomb E01234 49500 D202 Claire Buckman E39876 27800 D202 == Top 2 in group D050 John Rappl E21437 47000 D050 Nathan Adams E41298 21900 D050 == Top 2 in group D190 Kim Arlich E10001 57000 D190 Timothy Grove E16398 29900 D190 $ ./top-rank-by-group 4 == Top 4 in group D101 George Woltman E00127 53500 D101 David McClellan E04242 41500 D101 Tyler Bennett E10297 32000 D101 Tim Sampair E03033 27000 D101 == Top 4 in group D202 Rich Holcomb E01234 49500 D202 Claire Buckman E39876 27800 D202 David Motsinger E27002 19250 D202 Adam Smith E63535 18000 D202 == Top 4 in group D050 John Rappl E21437 47000 D050 Nathan Adams E41298 21900 D050 == Top 4 in group D190 Kim Arlich E10001 57000 D190 Timothy Grove E16398 29900 D190
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:
<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</lang>
+-----+-----+-----------------+------+ |ID |DEPT |NAME |SALARY| +-----+-----+-----------------+------+ |1013 |AB |de Bont C.A. |65000 | |1203 |AB |Anders H. |50000 | |1093 |AB |de Slegte S. |46987 | |1001 |AB |Janssen A.H. |41000 | +-----+-----+-----------------+------+ |100 |KA |Ezelbips P.J. |52000 | |101 |KA |'t Woud B. |45000 | |106 |KA |Pragtweik J.M.V. |42300 | |105 |KA |Clubdrager C. |39800 | |119 |KA |Tegenstroom H.L. |39000 | |107 |KA |Centjes R.M. |34000 | +-----+-----+-----------------+------+ |1199 |CC |Uitlaat G.A.S. |44500 | |1011 |CC |de Goeij J. |39000 | |1102 |CC |Zagt A. |33000 | |1107 |CC |Beloop L.O. |31000 | |1101 |CC |Modaal A.M.J. |30000 | |1106 |CC |Boon T.J. |25000 | +-----+-----+-----------------+------+ |1111 |CD |Telmans R.M. |55500 | |1026 |CD |Janszen H.P. |41000 | |1009 |CD |Janszoon A. |38665 | +-----+-----+-----------------+------+
using the data set:
Employees=: (<;.1~(1 1{.~#);+./@:(;:E.S:0])@:{.)];._2 noun define ID DEPT NAME SALARY 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 )
Named as a function where the (maximum) number of employees in each department is a parameter:
<lang j>reportTopSalaries=: 3 :'N , (<@:>"1@:|:@:((y <. #) {. ] \: SALARY)/.~ DEPT) |: <"1&> E'</lang>
- Output:
reportTopSalaries 2 +-----+-----+-----------------+------+ |ID |DEPT |NAME |SALARY| +-----+-----+-----------------+------+ |1013 |AB |de Bont C.A. |65000 | |1203 |AB |Anders H. |50000 | +-----+-----+-----------------+------+ |100 |KA |Ezelbips P.J. |52000 | |101 |KA |'t Woud B. |45000 | +-----+-----+-----------------+------+ |1199 |CC |Uitlaat G.A.S. |44500 | |1011 |CC |de Goeij J. |39000 | +-----+-----+-----------------+------+ |1111 |CD |Telmans R.M. |55500 | |1026 |CD |Janszen H.P. |41000 | +-----+-----+-----------------+------+
JavaScript
<lang javascript>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"},
];
function top_rank(n) {
var by_dept = group_by_dept(data); for (var dept in by_dept) { output(dept); for (var i = 0; i < n && i < by_dept[dept].length; i++) { var emp = by_dept[dept][i]; output(emp.name + ", id=" + emp.id + ", salary=" + emp.salary); } output(""); }
}
// group by dept, and sort by balary function group_by_dept(data) {
var by_dept = {}; for (var idx in data) { var dept = data[idx].dept; if ( ! has_property(by_dept, dept)) { by_dept[dept] = new Array(); } by_dept[dept].push(data[idx]); } for (var dept in by_dept) { // numeric sort by_dept[dept].sort(function (a,b){return b.salary - a.salary}); } return by_dept;
}
function has_property(obj, propname) {
return typeof(obj[propname]) != "undefined";
}
function output(str) {
try { WScript.Echo(str); // WSH } catch(err) { print(str); // Rhino }
}
top_rank(3);</lang>
- Output:
D101 George Woltman, id=E00127, salary=53500 David McClellan, id=E04242, salary=41500 Tyler Bennett, id=E10297, salary=32000 D050 John Rappl, id=E21437, salary=47000 Nathan Adams, id=E41298, salary=21900 D202 Rich Holcomb, id=E01234, salary=49500 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
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: <lang json> {
"Employee Name": "Tyler Bennett", "Employee ID": "E10297", "Salary": "32000", "Department": "D101" }</lang>
Program
<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 } ] );
</lang>
Example
With the above program, the top two salaries in each dapartment can be found as shown in the following transcript:<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" ] }
]</lang>
Lua
<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" }
}
dep = {} 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
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</lang>
- Output:
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 Department: D101 George Woltman E00127 53500 David McClellan E04242 41500
Mathematica
<lang 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"}, {"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"}};
TrimmedList=Map[ If[Length[#]>3,Take[#,3],#]& ,
Map[Reverse[SortBy[#,#3&]]&,GatherBy[InitialList,Last]]
];
Scan[((Print["Department ",#1,4,"\n","Employee","\t","Id","\t","Salary"]&[#])&[#];
(Scan[Print[#1,"\t",#2,"\t",#3]&,#] )& [#])&,TrimmedList]</lang>
- Output:
<pre>Department D101 Employee Id Salary George Woltman E00127 53500 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
Nimrod
<lang nimrod>import algorithm
type Record = tuple[name, id: string, salary: int, department: string]
var people: seq[Record] =
@[("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): int =
result = cmp(a.department, b.department) if result != 0: return result = cmp(b.salary, a.salary)
proc top(n) =
sort(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
top(2)</lang> Output:
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
OCaml
<lang ocaml>open StdLabels
let to_string (name,_,s,_) = (Printf.sprintf "%s (%d)" name s)
let take n li =
let rec aux i acc = function | _ when i >= n -> (List.rev acc) | [] -> (List.rev acc) | x::xs -> aux (succ i) (x::acc) xs in aux 0 [] li ;;
let toprank data n =
let len = List.length data in let h = Hashtbl.create len in List.iter data ~f:(fun ((_,_,_,dep) as employee) -> Hashtbl.add h dep employee); let deps = List.fold_left data ~init:[] ~f: (fun ac (_,_,_,v) -> if List.mem v ac then ac else v::ac) in let f dep = Printf.printf "Department: %s\n " dep; let l = Hashtbl.find_all h dep in let l2 = List.sort (fun (_,_,v1,_) (_,_,v2,_) -> compare v2 v1) l in let l3 = (take n l2) in print_endline(String.concat ", " (List.map to_string l3)); print_newline() in List.iter f deps;
let 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";
]
let () =
toprank data 3;
- </lang>
- Output:
Department: D190 Kim Arlich (57000), Timothy Grove (29900) Department: D202 Rich Holcomb (49500), Claire Buckman (27800), David Motsinger (19250) Department: D050 John Rappl (47000), Nathan Adams (21900) Department: D101 George Woltman (53500), David McClellan (41500), Tyler Bennett (32000)
--Bbsingapore 10:00, 13 January 2012 (UTC)
Oz
<lang oz>declare
%% Create a list of employee records. Data = {Map [['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]]
fun {$ [Name Id Salary Department]} employee(name:Name id:Id salary:Salary department:Department) end}
fun {TopEarners Employees N} {Record.map {GroupBy Employees department} fun {$ Employees}
{List.take {Sort Employees CompareSalary} N}
end} end fun {CompareSalary E1 E2} E1.salary > E2.salary end
%% Groups the records Xs by the value of feature F and returns %% the result as a record that maps values of F to list of records. fun {GroupBy Xs F} Groups = {Dictionary.new} in for X in Xs do Groups.(X.F) := X|{CondSelect Groups X.F nil} end {Dictionary.toRecord unit Groups} end
in
{Inspect {TopEarners Data 3}}</lang>
PARI/GP
- This code uses the select() function, which was added in PARI version 2.4.2. The order of the arguments changed between versions; to use in 2.4.2 change
select(function, vector)
toselect(vector, function)
.
<lang parigp>{V=[["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"]]};
top(n,V)={
my(dept=vecsort(vector(#V,i,V[i][4]),,8),d,v); for(i=1,#dept, d=dept[i]; print(d); v=select(u->u[4]==d,V); v=vecsort(v,3,4); \\ Sort by salary (#3) descending (flag 0x4) for(j=1,min(n,#v), print("\t",v[j][1],"\t",v[j][2],"\t",v[j][3]) ) );
};
top(2,V)</lang>
Pascal
<lang pascal>program TopRankPerGroup(output);
uses
Classes, Math;
type
TData = record name: string;
ID: string; salary: longint; dept: string end;
PTData = ^TData;
const
data: array [1..13] of TData = ( (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 CompareSalary(Item1, Item2: PTData): longint;
begin CompareSalary := Item2^.salary - Item1^.salary; end;
var
depts : TStringList; deptList: Tlist; number, i, j: integer;
begin
write ('Enter the number of ranks: '); readln (number); depts := TStringList.Create; depts.Sorted := true; depts.Duplicates := dupIgnore; for i := low(data) to high(data) do depts.Add(data[i].dept);
for i := 0 to depts.Count - 1 do begin writeln; writeln('Department: ', depts.Strings[i]); deptList := TList.Create; for j := low(data) to high(data) do if data[j].dept = depts.Strings[i] then deptList.Add(@data[j]); deptList.Sort(TListSortCompare(@CompareSalary)); for j := 0 to min(deptList.count, number) - 1 do begin write (PTData(deptList.Items[j])^.name, ', '); write ('ID: ', PTData(deptList.Items[j])^.ID, ', '); write ('Salary: ', PTData(deptList.Items[j])^.Salary); writeln; end; deptList.Destroy; end;
end.
</lang>
- Output:
% ./TopRankPerGroup Enter the number of ranks: 3 Department: D050 John Rappl, ID: E21437, Salary: 47000 Nathan Adams, ID: E41298, Salary: 21900 Department: D101 George Woltman, ID: E00127, Salary: 53500 David McClellan, ID: E04242, Salary: 41500 Tyler Bennett, ID: E10297, Salary: 32000 Department: D190 Kim Arlich, ID: E10001, Salary: 57000 Timothy Grove, ID: E16398, Salary: 29900 Department: D202 Rich Holcomb, ID: E01234, Salary: 49500 Claire Buckman, ID: E39876, Salary: 27800 David Motsinger, ID: E27002, Salary: 19250
Perl
<lang perl>sub zip {
my @a = @{shift()}; my @b = @{shift()}; my @l; push @l, shift @a, shift @b while @a and @b; return @l;
}
sub uniq {
my %h; grep {!$h{$_}++} @_;
}
my @data =
map {{ zip [qw(name id salary dept)], [split ','] }} split "\n", <<'EOF';
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 EOF
@ARGV or die "Please provide a value for N.\n"; my $N = shift;
foreach my $d (sort uniq map {$_->{dept}} @data) {
print "$d\n"; my @es = sort {$b->{salary} <=> $a->{salary}} grep {$_->{dept} eq $d} @data; foreach (1 .. $N) { @es or last; my $e = shift @es; printf "%-15s | %-6s | %5d\n", @{$e}{qw(name id salary)}; } print "\n";
}</lang>
Perl 6
We use tab-separated fields here from a heredoc; q:to/---/ begins the heredoc. The Z=> operator zips two lists into a list of pairs. In MAIN, the classify 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 ^ "up to" operator to take the first N elements of the sorted employee list. The :v modifier returns only valid values. The .<Name> form is a slice hash subscript with literals strings. That in turn is just the subscript form of the <...> ("quote words") form, which is more familar to Perl 5 programmers as qw/.../. 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 Perl 6 this means a method call on the current topic, $_, which is automatically set by any for or map construct that doesn't declare an explicit formal parameter on its closure.
<lang perl6>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(/ \t+ /) )
}
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; }
}</lang>
- Output:
D050 E21437 47000 John_Rappl D050 E41298 21900 Nathan_Adams D101 E00127 53500 George_Woltman D101 E04242 41500 David_McClellan D101 E10297 32000 Tyler_Bennett 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
PHP
<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 '
'; 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 '
';
}
top_sal(3);</lang>
- Output:
D050 Name ID Salary 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
PicoLisp
<lang PicoLisp># Employee Name, ID, Salary, Department (de *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) )
(de topEmployees (N)
(let Fmt (4 -16 -7 7) (for Dept (by cadddr group *Employees) (prinl "Department " (cadddr (car Dept)) ":") (tab Fmt NIL "Name" "ID" "Salary") (for (I . D) (flip (by caddr sort Dept)) (tab Fmt (pack I ". ") (car D) (cadr D) (caddr D)) (T (= I N)) ) (prinl) ) ) )
(topEmployees 3)</lang>
- Output:
Department D101: Name ID Salary 1. George Woltman E00127 53500 2. David McClellan E04242 41500 3. Tyler Bennett E10297 32000 Department D050: Name ID Salary 1. John Rappl E21437 47000 2. Nathan Adams E41298 21900 Department D202: Name ID Salary 1. Rich Holcomb E01234 49500 2. Claire Buckman E39876 27800 3. David Motsinger E27002 19250 Department D190: Name ID Salary 1. Kim Arlich E10001 57000 2. Timothy Grove E16398 29900
PL/I
<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;</lang>
- Output:
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
PL/SQL
<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;</lang>
PowerShell
<lang powershell>function New-Employee ($Name, $ID, $Salary, $Department) {
New-Object PSObject ` | Add-Member -PassThru NoteProperty EmployeeName $Name ` | Add-Member -PassThru NoteProperty EmployeeID $ID ` | Add-Member -PassThru NoteProperty Salary $Salary ` | Add-Member -PassThru NoteProperty Department $Department
}
$data = (New-Employee 'Tyler Bennett' E10297 32000 D101),
(New-Employee 'John Rappl' E21437 47000 D050), (New-Employee 'George Woltman' E00127 53500 D101), (New-Employee 'Adam Smith' E63535 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)
function Get-TopRank ($n) {
$data ` | Group-Object Department ` | ForEach-Object { $_.Group ` | Sort-Object Salary -Descending ` | Select-Object -First $n } ` | Format-Table -GroupBy Department
}</lang>
- Output:
PS> Get-TopRank 2 Department: D101 EmployeeName EmployeeID Salary Department ------------ ---------- ------ ---------- George Woltman E00127 53500 D101 David McClellan E04242 41500 D101 Department: D050 EmployeeName EmployeeID Salary Department ------------ ---------- ------ ---------- John Rappl E21437 47000 D050 Nathan Adams E41298 21900 D050 Department: D202 EmployeeName EmployeeID Salary Department ------------ ---------- ------ ---------- Rich Holcomb E01234 49500 D202 Claire Buckman E39876 27800 D202 Department: D190 EmployeeName EmployeeID Salary Department ------------ ---------- ------ ---------- Kim Arlich E10001 57000 D190 Timothy Grove E16398 29900 D190
PureBasic
<lang PureBasic>Structure Employees
Name$ ID$ Salary.i Department$
EndStructure
Procedure displayTopEarners(List MyEmployees.Employees(), n)
Protected filename$ = OpenFileRequester("Top rank per group", "DataFile.txt", "", 0) If ReadFile(0, filename$) Protected InData.Employees, txt.s, MaxNameLength While Eof(0) = 0 AddElement(MyEmployees()) txt = ReadString(0) With MyEmployees() \Name$ = StringField(txt, 1, ",") \ID$ = StringField(txt, 2, ",") \Salary = Val(StringField(txt, 3, ",")) \Department$ = StringField(txt, 4, ",") If Len(\Name$) > MaxNameLength: MaxNameLength = Len(\Name$): EndIf EndWith Wend CloseFile(0) Else MessageRequester("Information", "Couldn't open the file!") End EndIf
If OpenConsole() Protected OldDepartment$, count SortStructuredList(MyEmployees(), #PB_Sort_Descending, OffsetOf(Employees\Salary), #PB_Sort_integer) SortStructuredList(MyEmployees(), #PB_Sort_Ascending, OffsetOf(Employees\Department$), #PB_Sort_String) ForEach MyEmployees() With MyEmployees() If \Department$ <> OldDepartment$ If OldDepartment$ <> "" PrintN(#CRLF$) EndIf OldDepartment$ = \Department$ PrintN("Department " + \Department$ + #CRLF$ + "---------------") PrintN(LSet("Name", MaxNameLength + 3) + LSet("ID", 7) + LSet("Salary", 7)) count = 0 EndIf count + 1 If count <= n PrintN(LSet(\Name$, MaxNameLength + 1) + " " + RSet(\ID$, 7) + " $" + Str(\Salary)) EndIf EndWith Next PrintN(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input() EndIf
EndProcedure
NewList MyEmployees.Employees()
displayTopEarners(MyEmployees(), 3)</lang>
Save this as 'DataFile.txt and let the program open this 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 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
- Output:
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 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 Press ENTER to exit
Python
Python 2.7/3.x compatible. <lang python>from collections import defaultdict from heapq import nlargest
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')]
departments = defaultdict(list) for rec in data[1:]:
departments[rec[-1]].append(rec)
N = 3 format = " %-15s " * len(data[0]) for department, recs in sorted(departments.items()):
print ("Department %s" % department) print (format % data[0]) for rec in nlargest(N, recs, key=lambda rec: rec[-2]): print (format % rec)
print()</lang>
- Output:
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
Alternative Solution
Uses namedtuples for database records, and groupby builtin to group records by Department: <lang python>from collections import namedtuple from itertools import groupby
N = 2
db = 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
rows = db.split('\n') DBRecord = namedtuple('DBRecord', rows[0].replace(' ', '_')) 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])
for dpt, grp in groupby(records,
lambda record: record.Department))</lang>
- Output:
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') D101 DBRecord(Employee_Name='George Woltman', Employee_ID='E00127', Salary='53500', Department='D101') DBRecord(Employee_Name='David McClellan', Employee_ID='E04242', Salary='41500', Department='D101') D190 DBRecord(Employee_Name='Kim Arlich', Employee_ID='E10001', Salary='57000', Department='D190') DBRecord(Employee_Name='Timothy Grove', Employee_ID='E16398', Salary='29900', Department='D190') D202 DBRecord(Employee_Name='Rich Holcomb', Employee_ID='E01234', Salary='49500', Department='D202') DBRecord(Employee_Name='Claire Buckman', Employee_ID='E39876', Salary='27800', Department='D202')
R
First, read in the data. <lang R>dfr <- read.csv(tc <- textConnection( "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")); close(tc)</lang> To just return the top salary, it's very simple using tapply. <lang R>with(dfr, tapply(Salary, Department, max))</lang> To return N salaries, we replace max with our own function. <lang R>get.top.N.salaries <- function(N) {
with(dfr, tapply(Salary, Department, function(x) { sort(x); lx <- length(x) if(N >= lx) return(x) x[-1:(N-lx)] }))
}
get.top.N.salaries(3)</lang>
$D050 [1] 47000 21900 $D101 [1] 41500 15900 27000 $D190 [1] 57000 29900 $D202 [1] 27800 49500 19250
To return the whole record for each of the top salaries, a different tack is required. <lang R>get.top.N.salaries2 <- function(N) {
#Sort data frame by Department, then by Salary sorted <- dfr[with(dfr, order(Department, Salary, decreasing=TRUE)),] #Split the dataframe up, by Department bydept <- split(sorted, sorted$Department) #Return the first N values (or all of them lapply(bydept, function(x) { n <- min(N, nrow(x)) x[1:n,] })
}
get.top.N.salaries2(3)</lang>
- Output:
$D050 Employee.Name Employee.ID Salary Department 2 John Rappl E21437 47000 D050 8 Nathan Adams E41298 21900 D050 $D101 Employee.Name Employee.ID Salary Department 3 George Woltman E00127 53500 D101 6 David McClellan E04242 41500 D101 1 Tyler Bennett E10297 32000 D101 $D190 Employee.Name Employee.ID Salary Department 12 Kim Arlich E10001 57000 D190 13 Timothy Grove E16398 29900 D190 $D202 Employee.Name Employee.ID Salary Department 7 Rich Holcomb E01234 49500 D202 5 Claire Buckman E39876 27800 D202 10 David Motsinger E27002 19250 D202
Racket
<lang Racket>#lang racket
(struct employee (name id salary dept)) (define employees
(list (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")))
(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))))
</lang>
- Output:
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)
REXX
version 1
<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 the 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
do dep=1 for #d; say /*process each department. */ Xdept=word(depts,dep) /*current dept. being processed. */ do topN; highSal=0 /*process the top N salaries. */ 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; h=e /*We found a higher salary. */ end /*e*/
if h==0 then iterate /*no highest paid this time? */ say 'department: ' dept.h " $"sal.h id.h name.h /*show & tell.*/ dept.h= /*make sure we see employee again*/ end /*do topN*/ end /*dep*/ /*stick a fork in it, we're done.*/</lang>
output when the input is: 2
Finding top 2 salaries in each department. 13 employees, 4 departments: D101 D050 D202 D190 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
output when the input is: 100
Finding top 100 salaries in each department. 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
version 2
<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</lang>
output
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: 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
Ruby
Without much thought to report formatting:
<lang ruby>Employee = Struct.new(:name, :employee_id, :salary, :department)
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"),
]
def show_top_salaries_per_group(groups, n)
groups.each do |dept, emps| puts dept # sort by salary descending emps.sort_by {|emp| -emp.salary}.first(n).each do |e| puts " %-16s %6s %7d" % [e.name, e.employee_id, e.salary] end puts end
end
groups = employees.group_by {|emp| emp.department}.sort
show_top_salaries_per_group(groups,3)</lang>
- Output:
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
Run BASIC
<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</lang>
- Output:
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
Scala
Application idiomatic version
<lang scala>import scala.io.Source import scala.language.implicitConversions import scala.language.reflectiveCalls 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", "")) }
}</lang>
- Output:
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
using SLICK ORM
Version 1.3.1
<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</lang>
- Output:
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
SMEQL
The following SMEQL example returns the top 6 earners in each department based on this table schema:
<lang smeql>table: Employees
empID dept empName salary</lang> Source Code: <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)</lang>
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.
SQL
<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;</lang> <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;</lang>
- Output:
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
Note the T2 tie ranking aka ex aequo.
Tcl
<lang tcl>package require Tcl 8.5
set text {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}
set data [dict create] foreach line [split $text \n] {
lassign [split $line ,] name id salary dept dict lappend data $dept [list $name $id $salary]
}
proc top_n_salaries {n data} {
incr n -1 dict for {dept employees} $data { puts "Department $dept" foreach emp [lrange [lsort -integer -decreasing -index 2 $employees] 0 $n] { puts [format " %-20s %-8s %8d" {*}$emp] } puts "" }
}
top_n_salaries 3 $data</lang>
- Output:
Department D101 George Woltman E00127 53500 David McClellan E04242 41500 Tyler Bennett E10297 32000 Department D050 John Rappl E21437 47000 Nathan Adams E41298 21900 Department D202 Rich Holcomb E01234 49500 Claire Buckman E39876 27800 David Motsinger E27002 19250 Department D190 Kim Arlich E10001 57000 Timothy Grove E16398 29900
TUSCRIPT
<lang tuscript>$$ MODE TUSCRIPT MODE DATA $$ SET dates=* 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 $$ MODE TUSCRIPT SET nix=SPLIT (dates,":,:",EmployeeName,Employee_ID,Salary,Department) SET d=MIXED_SORT (department),d=REDUCE(d) SET index=DIGIT_INDEX(salary), index=REVERSE(index) SET employeeName=INDEX_SORT (employeeName,index) SET employee_ID =INDEX_SORT (employee_ID,index) SET Salary=INDEX_SORT (salary,index) SET Department=INDEX_SORT (Department,index) COMPILE LOOP l=d PRINT "Department ", l SET rtable=QUOTES (l) BUILD R_TABLE pos = rtable SET id=FILTER_INDEX (department,pos,-) RELEASE R_TABLE pos SET en =SELECT (employeeName,#id) SET ei =SELECT (employee_ID,#id) SET sal =SELECT (salary,#id) SET he =CENTER ("employeeName",-16) SET hi =CENTER ("employee ID",-11) SET hs =CENTER ("Salary",+10) SET line=REPEAT ("-",37) PRINT he,hi,hs PRINT line
LOOP e=en,i=ei,s=sal SET e=CENTER (e,-16), i=CENTER (i,-11), s=CENTER (s,+10) PRINT e,i,s ENDLOOP
PRINT " " ENDLOOP
ENDCOMPILE</lang>
- Output:
Department D050 employeeName employee ID Salary ------------------------------------- John Rappl E21437 47000 Nathan Adams E41298 21900 Department D101 employeeName employee ID Salary ------------------------------------- George Woltman E00127 53500 David McClellan E04242 41500 Tyler Bennett E10297 32000 Tim Sampair E03033 27000 Richard Potter E43128 15900 Department D190 employeeName employee ID Salary ------------------------------------- Kim Arlich E10001 57000 Timothy Grove E16398 29900 Department D202 employeeName employee ID Salary ------------------------------------- Rich Holcomb E01234 49500 Claire Buckman E39876 27800 David Motsinger E27002 19250 Adam Smith E63535 18000
TXR
Template Output Version
This version massages the data in a way that is suitable for generating the output template-wise with an @(output)
block.
The data is in a file, exactly as given in the problem. Parameter N is accepted from command line.
<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)</lang>
- Output:
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
Breakdown:
Descend into argument list:
<lang txr>@(next :args)</lang>
Collect first argument as n-param
variable:
<lang txr>@{n-param}</lang>
Drill into data file:
<lang txr>@(next "top-rank-per-group.dat")</lang>
Match header exactly:
<lang txr>Employee Name,Employee ID,Salary,Department</lang>
Now iterate over the data, requiring a variable called record
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 record
. The int-str
function converts the textual variable salary
to an integer:
<lang txr>@(collect :vars (record)) @name,@id,@salary,@dept @(bind record (@(int-str salary) dept name id)) @(end) </lang>
Next, we bind five variables to the output of some TXR Lisp code, which will return five lists:
<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])))</lang>
This code binds some successive variables. n
is an integer conversion of the command line argument.
dept-hash
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 dept
for later use. The ranked
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 @(output)
clause:
<lang txr>@(output) @ (repeat) Department: @dept @ (repeat)
@{name 15} (@id) $@{salary -6}
@ (end) @ (end) @(end)</lang>
Here, all these variables are individual lists. The dept
variable is a flat list; one nesting of @(repeat)
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.
<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}`)))))</lang>
Ursala
The algorithm used by the top function is to lex the data into fields, partition by the last field, 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. <lang Ursala>#import std
- import nat
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]-
top "n" = @tt sep`,*; mat0+ ^C(~&hz,mat`,*yS)*+ take/*"n"+ *zK2 (nleq+ %np~~)-<x&yzNC
- show+
main = top3 data</lang>
- Output:
D190 Kim Arlich,E10001,57000 Timothy Grove,E16398,29900 D101 George Woltman,E00127,53500 David McClellan,E04242,41500 Tyler Bennett,E10297,32000 D202 Rich Holcomb,E01234,49500 Claire Buckman,E39876,27800 David Motsinger,E27002,19250 D050 John Rappl,E21437,47000 Nathan Adams,E41298,21900
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). <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);</lang>
- Output:
D050: 47000,21900 D101: 53500,41500,32000 D190: 57000,29900 D202: 49500,27800,19250