Hash join: Difference between revisions

46,896 bytes added ,  1 month ago
Added FreeBASIC
(template "introheader" was renamed to "task heading")
(Added FreeBASIC)
 
(43 intermediate revisions by 25 users not shown)
Line 28:
'''let''' ''C'' = the output table (starts out empty)
'''for each''' row ''b'' '''in''' table ''B'' '''do:'''
'''place''' ''b'' '''in''' multimap ''M<sub>B</sub>'' under key ''b''(''j<sub>B</sub>'')
'''for each''' row ''a'' '''in''' table ''A'' '''do:'''
'''for each''' row ''b'' '''in''' multimap ''M<sub>B</sub>'' under key ''a''(''j<sub>A</sub>'')''':'''
'''let''' ''c'' = the concatenation of row ''a'' and row ''b''
'''place''' row ''c'' in table ''C''
Line 47:
{| style="border:none; border-collapse:collapse;"
|-
| style="border:none" | '''<math>A</math>''' =
| style="border:none" |
 
Line 66:
 
| style="border:none; padding-left:1.5em;" rowspan="2" |
| style="border:none" | <math>''B</math>'' =
| style="border:none" |
 
Line 85:
 
|-
| style="border:none" | ''j<mathsub>j_AA</mathsub>'' =
| style="border:none" | <code>Name</code> (i.e. column 1)
 
| style="border:none" | ''j<mathsub>j_BB</mathsub>'' =
| style="border:none" | <code>Character</code> (i.e. column 0)
|}
Line 119:
 
<br><hr>
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">F hash_join(table1, table2)
DefaultDict[String, [(Int, String)]] h
L(s) table1
h[s[1]].append(s)
 
[((Int, String), (String, String))] res
L(r) table2
L(s) h[r[0]]
res [+]= (s, r)
R res
 
V table1 = [(27, ‘Jonah’),
(18, ‘Alan’),
(28, ‘Glory’),
(18, ‘Popeye’),
(28, ‘Alan’)]
V table2 = [(‘Jonah’, ‘Whales’),
(‘Jonah’, ‘Spiders’),
(‘Alan’, ‘Ghosts’),
(‘Alan’, ‘Zombies’),
(‘Glory’, ‘Buffy’)]
 
L(row) hash_join(table1, table2)
print(row)</syntaxhighlight>
 
{{out}}
<pre>
((27, Jonah), (Jonah, Whales))
((27, Jonah), (Jonah, Spiders))
((18, Alan), (Alan, Ghosts))
((28, Alan), (Alan, Ghosts))
((18, Alan), (Alan, Zombies))
((28, Alan), (Alan, Zombies))
((28, Glory), (Glory, Buffy))
</pre>
 
=={{header|AppleScript}}==
{{Trans|JavaScript}}
 
Native AppleScript records lack introspection, but from Yosemite onwards we can read and write them a little more flexibly through the Foundation classes.
The vertical bars distinguish AppleScript reserved words ('''name''' and '''character''' here) from field name literal strings.
 
<syntaxhighlight lang="applescript">use framework "Foundation" -- Yosemite onwards, for record-handling functions
 
-- HASH JOIN -----------------------------------------------------------------
 
-- hashJoin :: [Record] -> [Record] -> String -> [Record]
on hashJoin(tblA, tblB, strJoin)
set {jA, jB} to splitOn("=", strJoin)
script instanceOfjB
on |λ|(a, x)
set strID to keyValue(x, jB)
set maybeInstances to keyValue(a, strID)
if maybeInstances is not missing value then
updatedRecord(a, strID, maybeInstances & {x})
else
updatedRecord(a, strID, [x])
end if
end |λ|
end script
set M to foldl(instanceOfjB, {name:"multiMap"}, tblB)
script joins
on |λ|(a, x)
set matches to keyValue(M, keyValue(x, jA))
if matches is not missing value then
script concat
on |λ|(row)
x & row
end |λ|
end script
a & map(concat, matches)
else
a
end if
end |λ|
end script
foldl(joins, {}, tblA)
end hashJoin
 
-- TEST ----------------------------------------------------------------------
on run
set lstA to [¬
{age:27, |name|:"Jonah"}, ¬
{age:18, |name|:"Alan"}, ¬
{age:28, |name|:"Glory"}, ¬
{age:18, |name|:"Popeye"}, ¬
{age:28, |name|:"Alan"}]
set lstB to [¬
{|character|:"Jonah", nemesis:"Whales"}, ¬
{|character|:"Jonah", nemesis:"Spiders"}, ¬
{|character|:"Alan", nemesis:"Ghosts"}, ¬
{|character|:"Alan", nemesis:"Zombies"}, ¬
{|character|:"Glory", nemesis:"Buffy"}, ¬
{|character|:"Bob", nemesis:"foo"}]
hashJoin(lstA, lstB, "name=character")
end run
 
 
-- RECORD FUNCTIONS ----------------------------------------------------------
 
-- keyValue :: String -> Record -> Maybe a
on keyValue(rec, strKey)
set ca to current application
set v to (ca's NSDictionary's dictionaryWithDictionary:rec)'s ¬
objectForKey:strKey
if v is not missing value then
item 1 of ((ca's NSArray's arrayWithObject:v) as list)
else
missing value
end if
end keyValue
 
-- updatedRecord :: Record -> String -> a -> Record
on updatedRecord(rec, strKey, varValue)
set ca to current application
set nsDct to (ca's NSMutableDictionary's dictionaryWithDictionary:rec)
nsDct's setValue:varValue forKey:strKey
item 1 of ((ca's NSArray's arrayWithObject:nsDct) as list)
end updatedRecord
 
 
-- GENERIC FUNCTIONS ---------------------------------------------------------
 
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
 
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
 
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
 
-- splitOn :: Text -> Text -> [Text]
on splitOn(strDelim, strMain)
set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
set lstParts to text items of strMain
set my text item delimiters to dlm
return lstParts
end splitOn</syntaxhighlight>
{{Out}}
<pre>{{age:27, |name|:"Jonah", |character|:"Jonah", nemesis:"Whales"},
{age:27, |name|:"Jonah", |character|:"Jonah", nemesis:"Spiders"},
{age:18, |name|:"Alan", |character|:"Alan", nemesis:"Ghosts"},
{age:18, |name|:"Alan", |character|:"Alan", nemesis:"Zombies"},
{age:28, |name|:"Glory", |character|:"Glory", nemesis:"Buffy"},
{age:28, |name|:"Alan", |character|:"Alan", nemesis:"Ghosts"},
{age:28, |name|:"Alan", |character|:"Alan", nemesis:"Zombies"}}</pre>
 
=={{header|Arturo}}==
<syntaxhighlight lang="arturo">hashJoin: function [t1, t2][
result: []
h: #[]
loop t1 's [
if not? key? h s\1 -> h\[s\1]: []
h\[s\1]: h\[s\1] ++ @[s]
]
loop t2 'r [
loop h\[r\0] 's [
'result ++ @[@[s r]]
]
]
return result
]
 
table1: [
[27 "Jonah"]
[18 "Alan"]
[28 "Glory"]
[18 "Popeye"]
[28 "Alan"]
]
 
table2: [
["Jonah" "Whales"]
["Jonah" "Spiders"]
["Alan" "Ghosts"]
["Alan" "Zombies"]
["Glory" "Buffy"]
]
 
loop hashJoin table1 table2 'row ->
print row</syntaxhighlight>
 
{{out}}
 
<pre>[27 Jonah] [Jonah Whales]
[27 Jonah] [Jonah Spiders]
[18 Alan] [Alan Ghosts]
[28 Alan] [Alan Ghosts]
[18 Alan] [Alan Zombies]
[28 Alan] [Alan Zombies]
[28 Glory] [Glory Buffy] </pre>
 
=={{header|AWK}}==
<syntaxhighlight lang="awk">
# syntax: GAWK -f HASH_JOIN.AWK [-v debug={0|1}] TABLE_A TABLE_B
#
# sorting:
# PROCINFO["sorted_in"] is used by GAWK
# SORTTYPE is used by Thompson Automation's TAWK
#
BEGIN {
FS = ","
PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
if (ARGC-1 != 2) {
print("error: incorrect number of arguments") ; errors++
exit # go to END
}
}
{ if (NR == FNR) { # table A
if (FNR == 1) {
a_head = prefix_column_names("A")
next
}
a_arr[$2][$1] = $0 # [name][age]
}
if (NR != FNR) { # table B
if (FNR == 1) {
b_head = prefix_column_names("B")
next
}
b_arr[$1][$2] = $0 # [character][nemesis]
}
}
END {
if (errors > 0) { exit(1) }
if (debug == 1) {
dump_table(a_arr,a_head)
dump_table(b_arr,b_head)
}
printf("%s%s%s\n",a_head,FS,b_head) # table heading
for (i in a_arr) {
if (i in b_arr) {
for (j in a_arr[i]) {
for (k in b_arr[i]) {
print(a_arr[i][j] FS b_arr[i][k]) # join table A & table B
}
}
}
}
exit(0)
}
function dump_table(arr,heading, i,j) {
printf("%s\n",heading)
for (i in arr) {
for (j in arr[i]) {
printf("%s\n",arr[i][j])
}
}
print("")
}
function prefix_column_names(p, tmp) {
tmp = p "." $0
gsub(/,/,"&" p ".",tmp)
return(tmp)
}
</syntaxhighlight>
<p>TABLE_A input:</p>
<pre>
Age,Name
27,Jonah
18,Alan
28,Glory
18,Popeye
28,Alan
</pre>
<p>TABLE_B input:</p>
<pre>
Character,Nemesis
Jonah,Whales
Jonah,Spiders
Alan,Ghosts
Alan,Zombies
Glory,Buffy
</pre>
{{out}}
<pre>
A.Age,A.Name,B.Character,B.Nemesis
18,Alan,Alan,Ghosts
18,Alan,Alan,Zombies
28,Alan,Alan,Ghosts
28,Alan,Alan,Zombies
28,Glory,Glory,Buffy
27,Jonah,Jonah,Spiders
27,Jonah,Jonah,Whales
</pre>
 
=={{header|Bracmat}}==
This solution creates a hash table for the smaller relation in the function <code>join</code>. This function takes as arguments the smallest table, the biggest table and then three pieces of code: two patterns that describe each table's field order and code that generates one row of output. These pieces of code are inserted in a fixed skeleton of code using macro substitution.
<langsyntaxhighlight lang="bracmat">( (27.Jonah)
(18.Alan)
(28.Glory)
Line 178 ⟶ 503:
)
&
);</langsyntaxhighlight>
Output:
<pre> (28.Alan.Ghosts)
Line 190 ⟶ 515:
=={{header|C sharp}}==
;using LINQ to Objects
<langsyntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
using System.Linq;
Line 289 ⟶ 614:
}
}
}</langsyntaxhighlight>
 
{{out}}
Line 300 ⟶ 625:
Age: 28, Name: Alan, Nemesis: Ghosts
Age: 28, Name: Alan, Nemesis: Zombies
</pre>
 
=={{header|C++}}==
<syntaxhighlight lang="cpp">#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
 
using tab_t = std::vector<std::vector<std::string>>;
tab_t tab1 {
// Age Name
{"27", "Jonah"}
, {"18", "Alan"}
, {"28", "Glory"}
, {"18", "Popeye"}
, {"28", "Alan"}
};
 
tab_t tab2 {
// Character Nemesis
{"Jonah", "Whales"}
, {"Jonah", "Spiders"}
, {"Alan", "Ghosts"}
, {"Alan", "Zombies"}
, {"Glory", "Buffy"}
};
 
std::ostream& operator<<(std::ostream& o, const tab_t& t) {
for(size_t i = 0; i < t.size(); ++i) {
o << i << ":";
for(const auto& e : t[i])
o << '\t' << e;
o << std::endl;
}
return o;
}
 
tab_t Join(const tab_t& a, size_t columna, const tab_t& b, size_t columnb) {
std::unordered_multimap<std::string, size_t> hashmap;
// hash
for(size_t i = 0; i < a.size(); ++i) {
hashmap.insert(std::make_pair(a[i][columna], i));
}
// map
tab_t result;
for(size_t i = 0; i < b.size(); ++i) {
auto range = hashmap.equal_range(b[i][columnb]);
for(auto it = range.first; it != range.second; ++it) {
tab_t::value_type row;
row.insert(row.end() , a[it->second].begin() , a[it->second].end());
row.insert(row.end() , b[i].begin() , b[i].end());
result.push_back(std::move(row));
}
}
return result;
}
 
int main(int argc, char const *argv[])
{
using namespace std;
int ret = 0;
cout << "Table A: " << endl << tab1 << endl;
cout << "Table B: " << endl << tab2 << endl;
auto tab3 = Join(tab1, 1, tab2, 0);
cout << "Joined tables: " << endl << tab3 << endl;
return ret;
}
 
</syntaxhighlight>
{{out}}
<pre>Table A:
0: 27 Jonah
1: 18 Alan
2: 28 Glory
3: 18 Popeye
4: 28 Alan
 
Table B:
0: Jonah Whales
1: Jonah Spiders
2: Alan Ghosts
3: Alan Zombies
4: Glory Buffy
 
Joined tables:
0: 27 Jonah Jonah Whales
1: 27 Jonah Jonah Spiders
2: 28 Alan Alan Ghosts
3: 18 Alan Alan Ghosts
4: 28 Alan Alan Zombies
5: 18 Alan Alan Zombies
6: 28 Glory Glory Buffy
</pre>
 
=={{header|Clojure}}==
<langsyntaxhighlight lang="clojure">
(defn hash-join [table1 col1 table2 col2]
(let [hashed (group-by col1 table1)]
Line 324 ⟶ 741:
 
(pprint (sort-by :name (hash-join s :name r :name)))
</syntaxhighlight>
</lang>
 
{{out}}
Line 339 ⟶ 756:
 
=={{header|Common Lisp}}==
<langsyntaxhighlight lang="lisp">(defparameter *table-A* '((27 "Jonah") (18 "Alan") (28 "Glory") (18 "Popeye") (28 "Alan")))
 
(defparameter *table-B* '(("Jonah" "Whales") ("Jonah" "Spiders") ("Alan" "Ghosts") ("Alan" "Zombies") ("Glory" "Buffy")))
Line 355 ⟶ 772:
(let ((val (car (gethash i *hash-table*))))
(loop for (a b) in val do
(format t "{~a ~a} {~a ~a}~%" a b i r))))</langsyntaxhighlight>
 
{{out}}
Line 370 ⟶ 787:
=={{header|D}}==
{{trans|Python}}
<langsyntaxhighlight lang="d">import std.stdio, std.typecons;
 
auto hashJoin(size_t index1, size_t index2, T1, T2)
Line 404 ⟶ 821:
foreach (const row; hashJoin!(1, 0)(table1, table2))
writefln("(%s, %5s) (%5s, %7s)", row[0][], row[1][]);
}</langsyntaxhighlight>
{{out}}
<pre>(27, Jonah) (Jonah, Whales)
Line 416 ⟶ 833:
=={{header|Déjà Vu}}==
{{trans|Python}}
<langsyntaxhighlight lang="dejavu">hashJoin table1 index1 table2 index2:
local :h {}
# hash phase
Line 434 ⟶ 851:
 
for row in hashJoin table1 1 table2 0:
!. row</langsyntaxhighlight>
{{out}}
<pre>[ [ 27 "Jonah" ] [ "Jonah" "Whales" ] ]
Line 447 ⟶ 864:
=={{header|EchoLisp}}==
Since this is a real, professional application, we build the hash tables in permanent (local) storage.
<langsyntaxhighlight lang="lisp">
(define ages '((27 "Jonah") (18 "Alan") (28 "Glory") (18 "Popeye") (28 "Alan")))
(define nemesis '(("Jonah" "Whales") ("Jonah" "Spiders") ("Alan" "Ghosts") ("Alan" "Zombies") ("Glory" "Buffy")))
Line 475 ⟶ 892:
(n (local-get-value k 'NEMESIS)))
(writeln a n))
</syntaxhighlight>
</lang>
{{out}}
<langsyntaxhighlight lang="lisp">
(28 "Alan") ("Alan" "Zombies")
(28 "Alan") ("Alan" "Ghosts")
Line 485 ⟶ 902:
(27 "Jonah") ("Jonah" "Spiders")
(27 "Jonah") ("Jonah" "Whales")
</syntaxhighlight>
</lang>
 
=={{header|ECL}}==
<syntaxhighlight lang="ecl">
<lang ECL>
LeftRec := RECORD
UNSIGNED1 Age;
Line 522 ⟶ 939:
27 Jonah Spiders
*/
</syntaxhighlight>
</lang>
 
=={{header|Elixir}}==
{{trans|Ruby}}
<langsyntaxhighlight lang="elixir">defmodule Hash do
def join(table1, index1, table2, index2) do
h = Enum.group_by(table1, fn s -> elem(s, index1) end)
Line 545 ⟶ 962:
{"Alan", "Zombies"},
{"Glory", "Buffy"}]
Hash.join(table1, 1, table2, 0) |> Enum.each(&IO.inspect &1)</langsyntaxhighlight>
 
{{out}}
Line 559 ⟶ 976:
 
=={{header|Erlang}}==
<syntaxhighlight lang="erlang">
<lang Erlang>
-module( hash_join ).
 
Line 577 ⟶ 994:
dict_find( error, _Key, _Value ) -> [];
dict_find( {ok, Values}, Key, Value ) -> [{X, {Key, Value}} || X <- Values].
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 589 ⟶ 1,006:
{{28,"Glory"},{"Glory","Buffy"}}]
</pre>
 
=={{header|Emacs Lisp}}==
<syntaxhighlight lang="lisp">
(defun make-multi-map (rows)
(let ((multi-map nil))
(cl-loop for row in rows do
(let* ((name (car row))
(name-list (assoc name multi-map)))
(if name-list
(nconc name-list (list row))
(progn
(add-to-list 'multi-map (list name row) 't) ) ) ) )
multi-map) )
 
(defun join-tables (table1 table2)
(let ((multi-map (make-multi-map table2))
(result-table '()))
(cl-loop for row in table1 do
(let ((multi-rc (assoc (cdr row) multi-map)))
(when multi-rc
(cl-loop for multi-line in (cdr multi-rc) do
(add-to-list 'result-table
(list (car row) (cdr row) (car multi-line) (cdr multi-line))
't)))))
result-table))
 
(let ((table1 '((27 . "Jonah")
(18 . "Alan")
(28 . "Glory")
(18 . "Popeye")
(28 . "Alan")))
(table2 '(("Jonah" . "Whales")
("Jonah" . "Spiders")
("Alan" . "Ghosts")
("Alan" . "Zombies")
("Glory" . "Buffy"))))
(message "%s" (join-tables table1 table2)) )
</syntaxhighlight>
 
=={{header|FreeBASIC}}==
<syntaxhighlight lang="vbnet">Type Data1
value As Integer
key As String
End Type
 
Type Data2
key As String
value As String
End Type
 
Dim table1(5) As Data1
Dim table2(5) As Data2
 
table1(1).value = 27: table1(1).key = "Jonah"
table1(2).value = 18: table1(2).key = "Alan"
table1(3).value = 28: table1(3).key = "Glory"
table1(4).value = 18: table1(4).key = "Popeye"
table1(5).value = 28: table1(5).key = "Alan"
 
table2(1).key = "Jonah": table2(1).value = "Whales"
table2(2).key = "Jonah": table2(2).value = "Spiders"
table2(3).key = "Alan": table2(3).value = "Ghosts"
table2(4).key = "Alan": table2(4).value = "Zombies"
table2(5).key = "Glory": table2(5).value = "Buffy"
 
Print String(51, "-")
Print " Age | Name || Name | Nemesis"
Print String(51, "-")
 
For i As Integer = 1 To 5
For j As Integer = 1 To 5
If table1(i).key = table2(j).key Then
Print Using " ## | \ \ || \ \ | \ \"; table1(i).value; table1(i).key; table2(j).key; table2(j).value
End If
Next j
Next i
 
Sleep</syntaxhighlight>
{{out}}
<pre>---------------------------------------------------
Age | Name || Name | Nemesis
---------------------------------------------------
27 | Jonah || Jonah | Whales
27 | Jonah || Jonah | Spiders
18 | Alan || Alan | Ghosts
18 | Alan || Alan | Zombies
28 | Glory || Glory | Buffy
28 | Alan || Alan | Ghosts
28 | Alan || Alan | Zombies</pre>
 
=={{header|F_Sharp|F#}}==
<syntaxhighlight lang="fsharp">[<EntryPoint>]
let main argv =
let table1 = [27, "Jonah";
18, "Alan";
28, "Glory";
18, "Popeye";
28, "Alan"]
let table2 = ["Jonah", "Whales";
"Jonah", "Spiders";
"Alan", "Ghosts";
"Alan", "Zombies";
"Glory", "Buffy"]
let hash = Seq.groupBy (fun r -> snd r) table1
table2
|> Seq.collect (fun r ->
hash
|> Seq.collect (fun kv ->
if (fst r) <> (fst kv) then []
else (Seq.map (fun x -> (x, r)) (snd kv)) |> Seq.toList)
)
|> Seq.toList
|> printfn "%A"
0</syntaxhighlight>
{{out}}
<pre>[((27, "Jonah"), ("Jonah", "Whales")); ((27, "Jonah"), ("Jonah", "Spiders"));
((18, "Alan"), ("Alan", "Ghosts")); ((28, "Alan"), ("Alan", "Ghosts"));
((18, "Alan"), ("Alan", "Zombies")); ((28, "Alan"), ("Alan", "Zombies"));
((28, "Glory"), ("Glory", "Buffy"))]</pre>
 
=={{header|Forth}}==
Line 596 ⟶ 1,132:
Needs the FMS-SI (single inheritance) library code located here:
http://soton.mpeforth.com/flag/fms/index.html
<langsyntaxhighlight lang="forth">
include FMS-SI.f
include FMS-SILib.f
Line 682 ⟶ 1,218:
q free:
 
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 693 ⟶ 1,229:
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package main
 
import "fmt"
Line 723 ⟶ 1,259:
}
}
}</langsyntaxhighlight>
{{out}}
<pre>
Line 738 ⟶ 1,274:
 
Semi-imperative style:
<syntaxhighlight lang="groovy">
<lang Groovy>
def hashJoin(table1, col1, table2, col2) {
 
Line 754 ⟶ 1,290:
q
}
</syntaxhighlight>
</lang>
 
More functional style:
<syntaxhighlight lang="groovy">
<lang Groovy>
def hashJoin(table1, col1, table2, col2) {
 
Line 766 ⟶ 1,302:
}.flatten()
}
</syntaxhighlight>
</lang>
 
Sample run (either version as the result is the same):
<syntaxhighlight lang="groovy">
<lang Groovy>
def s = [[age: 27, name: 'Jonah'],
[age: 18, name: 'Alan'],
Line 783 ⟶ 1,319:
 
hashJoin(s, "name", r, "name").sort {it.name}.each { println it }
</syntaxhighlight>
</lang>
 
produces:
Line 804 ⟶ 1,340:
 
Placing all relations with the same selector value in a list in the hashtable allows us to join many to one/many relations.
<langsyntaxhighlight Haskelllang="haskell">{-# LANGUAGE LambdaCase, TupleSections #-}
import qualified Data.HashTable.ST.Basic as H
import Data.Hashable
Line 832 ⟶ 1,368:
("Alan", "Ghosts"), ("Alan", "Zombies"), ("Glory", "Buffy")]
fst
</syntaxhighlight>
</lang>
<pre>
((3,"Glory"),("Glory","Buffy"))
Line 842 ⟶ 1,378:
 
The task require hashtables; however, a cleaner and more functional solution would be to use Data.Map (based on binary trees):
<langsyntaxhighlight Haskelllang="haskell">{-# LANGUAGE TupleSections #-}
import qualified Data.Map as M
import Data.List
Line 860 ⟶ 1,396:
("Alan", "Ghosts"), ("Alan", "Zombies"), ("Glory", "Buffy")]
fst
</syntaxhighlight>
</lang>
<pre>
((1,"Jonah"),("Jonah","Spiders"))
Line 873 ⟶ 1,409:
Data:
 
<langsyntaxhighlight Jlang="j">table1=: ;:;._2(0 :0)
27 Jonah
18 Alan
Line 887 ⟶ 1,423:
Alan Zombies
Glory Buffy
)</langsyntaxhighlight>
 
The task does not specify the hash function to use, so we'll use an identity function. But [[SHA-1]] could be used instead, with a little more work (you'd need to convert the name into the bit vector needed by the SHA-1 interface). Practically speaking, though, the only benefit of SHA-1 in this context would be to slow down the join.
Line 893 ⟶ 1,429:
Implementation:
 
<langsyntaxhighlight Jlang="j">hash=: ]
dojoin=:3 :0
c1=. {.{.y
Line 901 ⟶ 1,437:
)
 
JOIN=: ; -.&a: ,/each(hash@{."1 <@dojoin/. ]) (1 1 0&#inv@|."1 table1), 1 0 1#inv"1 table2</langsyntaxhighlight>
 
Result:
 
<langsyntaxhighlight Jlang="j"> JOIN
┌─────┬──┬───────┐
│Jonah│27│Whales │
Line 920 ⟶ 1,456:
├─────┼──┼───────┤
│Glory│28│Buffy │
└─────┴──┴───────┘</langsyntaxhighlight>
 
=={{header|Java}}==
{{trans|PHP}}
{{works with|Java|8}}
<langsyntaxhighlight lang="java">import java.util.*;
 
public class HashJoin {
Line 964 ⟶ 1,500:
return result;
}
}</langsyntaxhighlight>
 
<pre>[[27, Jonah], [Jonah, Whales]]
Line 973 ⟶ 1,509:
[[28, Alan], [Alan, Zombies]]
[[28, Glory], [Glory, Buffy]]</pre>
 
=={{header|JavaScript}}==
===ES6===
 
<syntaxhighlight lang="javascript">(() => {
'use strict';
 
// hashJoin :: [Dict] -> [Dict] -> String -> [Dict]
let hashJoin = (tblA, tblB, strJoin) => {
 
let [jA, jB] = strJoin.split('='),
M = tblB.reduce((a, x) => {
let id = x[jB];
return (
a[id] ? a[id].push(x) : a[id] = [x],
a
);
}, {});
 
return tblA.reduce((a, x) => {
let match = M[x[jA]];
return match ? (
a.concat(match.map(row => dictConcat(x, row)))
) : a;
}, []);
},
 
// dictConcat :: Dict -> Dict -> Dict
dictConcat = (dctA, dctB) => {
let ok = Object.keys;
return ok(dctB).reduce(
(a, k) => (a['B_' + k] = dctB[k]) && a,
ok(dctA).reduce(
(a, k) => (a['A_' + k] = dctA[k]) && a, {}
)
);
};
 
 
// TEST
let lstA = [
{ age: 27, name: 'Jonah' },
{ age: 18, name: 'Alan' },
{ age: 28, name: 'Glory' },
{ age: 18, name: 'Popeye' },
{ age: 28, name: 'Alan' }
],
lstB = [
{ character: 'Jonah', nemesis: 'Whales' },
{ character: 'Jonah', nemesis: 'Spiders' },
{ character: 'Alan', nemesis: 'Ghosts' },
{ character:'Alan', nemesis: 'Zombies' },
{ character: 'Glory', nemesis: 'Buffy' },
{ character: 'Bob', nemesis: 'foo' }
];
 
return hashJoin(lstA, lstB, 'name=character');
})();
</syntaxhighlight>
 
{{Out}}
<pre>[{"A_age":27,"A_name":"Jonah","B_character":"Jonah","B_nemesis":"Whales"},
{"A_age":27,"A_name":"Jonah","B_character":"Jonah","B_nemesis":"Spiders"},
{"A_age":18,"A_name":"Alan","B_character":"Alan","B_nemesis":"Ghosts"},
{"A_age":18,"A_name":"Alan","B_character":"Alan","B_nemesis":"Zombies"},
{"A_age":28,"A_name":"Glory","B_character":"Glory","B_nemesis":"Buffy"},
{"A_age":28,"A_name":"Alan","B_character":"Alan","B_nemesis":"Ghosts"},
{"A_age":28,"A_name":"Alan","B_character":"Alan","B_nemesis":"Zombies"}]</pre>
 
=={{header|jq}}==
Line 989 ⟶ 1,594:
 
===hashJoin===
<langsyntaxhighlight lang="jq"># hashJoin(table1; key1; table2; key2) expects the two tables to be
# arrays, either of JSON objects, or of arrays.
 
Line 1,023 ⟶ 1,628:
reduce $hash[$key][] as $r (.; . + [ $row + $r ] )
else . end)
;</langsyntaxhighlight>
 
'''Example'''
<langsyntaxhighlight lang="jq">def table1:
[ {"age": 27, "name": "Jonah"},
{"age": 18, "name": "Alan"},
Line 1,064 ⟶ 1,669:
( hashJoin(table1; "name"; table2; "name"),
hashJoin(table1a; 1; table2a; 0)
) | pp</langsyntaxhighlight>
{{out}}
<langsyntaxhighlight lang="sh">$ jq -c -r -n -f HashJoin.jq
 
{"age":27,"name":"Jonah","nemesis":"Whales"}
Line 1,078 ⟶ 1,683:
[28,"Alan","Alan","Ghosts"]
[28,"Alan","Alan","Zombies"]
[28,"Glory","Glory","Buffy"]</langsyntaxhighlight>
 
===hashJoinArrays===
<langsyntaxhighlight lang="jq"># The tables should be arrays of arrays;
# index1 and index2 should be the 0-based indices of the join columns.
#
Line 1,107 ⟶ 1,712:
. + [ $r + $row[0:index2] + $row[index2+1:] ] )
else . end)
;</langsyntaxhighlight>
'''Example'''
 
In the following example, the previously defined pretty-print function (pp) and tables (table1 and table2)
are used, so their definitions are not repeated here.
<langsyntaxhighlight lang="jq">hashJoinArrays(table1; 1; table2; 0) | pp</langsyntaxhighlight>
{{out}}
<langsyntaxhighlight lang="sh">$ jq -c -r -n -f HashJoinArrays.jq
 
[27,"Jonah","Whales"]
Line 1,120 ⟶ 1,725:
[28,"Alan","Ghosts"]
[28,"Alan","Zombies"]
[28,"Glory","Buffy"]</langsyntaxhighlight>
 
=={{header|Julia}}==
{{works with|Julia|0.6}}
For dataframes there is a builtin function join:
<syntaxhighlight lang="julia">using DataFrames
 
A = DataFrame(Age = [27, 18, 28, 18, 28], Name = ["Jonah", "Alan", "Glory", "Popeye", "Alan"])
B = DataFrame(Name = ["Jonah", "Jonah", "Alan", "Alan", "Glory"],
Nemesis = ["Whales", "Spiders", "Ghosts", "Zombies", "Buffy"])
AB = join(A, B, on = :Name)
 
@show A B AB</syntaxhighlight>
 
{{out}}
<pre>A = 5×2 DataFrames.DataFrame
│ Row │ Age │ Name │
├─────┼─────┼──────────┤
│ 1 │ 27 │ "Jonah" │
│ 2 │ 18 │ "Alan" │
│ 3 │ 28 │ "Glory" │
│ 4 │ 18 │ "Popeye" │
│ 5 │ 28 │ "Alan" │
B = 5×2 DataFrames.DataFrame
│ Row │ Name │ Nemesis │
├─────┼─────────┼───────────┤
│ 1 │ "Jonah" │ "Whales" │
│ 2 │ "Jonah" │ "Spiders" │
│ 3 │ "Alan" │ "Ghosts" │
│ 4 │ "Alan" │ "Zombies" │
│ 5 │ "Glory" │ "Buffy" │
AB = 7×3 DataFrames.DataFrame
│ Row │ Age │ Name │ Nemesis │
├─────┼─────┼─────────┼───────────┤
│ 1 │ 18 │ "Alan" │ "Ghosts" │
│ 2 │ 18 │ "Alan" │ "Zombies" │
│ 3 │ 28 │ "Alan" │ "Ghosts" │
│ 4 │ 28 │ "Alan" │ "Zombies" │
│ 5 │ 28 │ "Glory" │ "Buffy" │
│ 6 │ 27 │ "Jonah" │ "Whales" │
│ 7 │ 27 │ "Jonah" │ "Spiders" │</pre>
 
Following the task hint:
<syntaxhighlight lang="julia">function hashjoin(A::Array, ja::Int, B::Array, jb::Int)
M = Dict(t[jb] => filter(l -> l[jb] == t[jb], B) for t in B)
return collect([a, b] for a in A for b in get(M, a[ja], ()))
end
 
table1 = [(27, "Jonah"),
(18, "Alan"),
(28, "Glory"),
(18, "Popeye"),
(28, "Alan")]
table2 = [("Jonah", "Whales"),
("Jonah", "Spiders"),
("Alan", "Ghosts"),
("Alan", "Zombies"),
("Glory", "Buffy")]
 
for r in hashjoin(table1, 2, table2, 1)
println(r)
end</syntaxhighlight>
 
{{out}}
<pre>Tuple{Any,String}[(27, "Jonah"), ("Jonah", "Whales")]
Tuple{Any,String}[(27, "Jonah"), ("Jonah", "Spiders")]
Tuple{Any,String}[(18, "Alan"), ("Alan", "Ghosts")]
Tuple{Any,String}[(18, "Alan"), ("Alan", "Zombies")]
Tuple{Any,String}[(28, "Glory"), ("Glory", "Buffy")]
Tuple{Any,String}[(28, "Alan"), ("Alan", "Ghosts")]
Tuple{Any,String}[(28, "Alan"), ("Alan", "Zombies")]</pre>
 
=={{header|Kotlin}}==
<syntaxhighlight lang="scala">data class A(val age: Int, val name: String)
 
data class B(val character: String, val nemesis: String)
 
data class C(val rowA: A, val rowB: B)
 
fun hashJoin(tableA: List<A>, tableB: List<B>): List<C> {
val mm = tableB.groupBy { it.character }
val tableC = mutableListOf<C>()
for (a in tableA) {
val value = mm[a.name] ?: continue
for (b in value) tableC.add(C(a, b))
}
return tableC.toList()
}
 
fun main(args: Array<String>) {
val tableA = listOf(
A(27, "Jonah"),
A(18, "Alan"),
A(28, "Glory"),
A(18, "Popeye"),
A(28, "Alan")
)
val tableB = listOf(
B("Jonah", "Whales"),
B("Jonah", "Spiders"),
B("Alan", "Ghosts"),
B("Alan", "Zombies"),
B("Glory", "Buffy")
)
val tableC = hashJoin(tableA, tableB)
println("A.Age A.Name B.Character B.Nemesis")
println("----- ------ ----------- ---------")
for (c in tableC) {
print("${c.rowA.age} ${c.rowA.name.padEnd(6)} ")
println("${c.rowB.character.padEnd(6)} ${c.rowB.nemesis}")
}
}</syntaxhighlight>
 
{{out}}
<pre>
A.Age A.Name B.Character B.Nemesis
----- ------ ----------- ---------
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
18 Alan Alan Zombies
28 Glory Glory Buffy
28 Alan Alan Ghosts
28 Alan Alan Zombies
</pre>
=={{header|Lua}}==
===Literal===
Lua tables are implemented with hash keys, so this task is a bit anti-idiomatic for Lua. That is, if you knew in advance that this would be the primary operation on the data, then you'd likely (re-)structure the data to directly support it. But, to comply with the intent of the task, the data here is initially structured as an indexed (rather than hash-keyed) array, <i>then</i> hashed dynamically. (it's analogous to the Python solution, where a list is immediately converted to a dictionary - but could have <i>began</i> as a dictionary)
<syntaxhighlight lang="lua">local function recA(age, name) return { Age=age, Name=name } end
local tabA = { recA(27,"Jonah"), recA(18,"Alan"), recA(28,"Glory"), recA(18,"Popeye"), recA(28,"Alan") }
 
local function recB(character, nemesis) return { Character=character, Nemesis=nemesis } end
local tabB = { recB("Jonah","Whales"), recB("Jonah","Spiders"), recB("Alan","Ghosts"), recB("Alan","Zombies"), recB("Glory","Buffy") }
 
local function hashjoin(taba, cola, tabb, colb)
local hash, join = {}, {}
for _,rowa in pairs(taba) do
if (not hash[rowa[cola]]) then hash[rowa[cola]] = {} end
table.insert(hash[rowa[cola]], rowa)
end
for _,rowb in pairs(tabb) do
for _,rowa in pairs(hash[rowb[colb]]) do
join[#join+1] = { A=rowa, B=rowb }
end
end
return join
end
 
for _,row in pairs(hashjoin(tabA, "Name", tabB, "Character")) do
print(row.A.Age, row.A.Name, row.B.Character, row.B.Nemesis)
end</syntaxhighlight>
{{out}}
<pre>27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
28 Alan Alan Ghosts
18 Alan Alan Zombies
28 Alan Alan Zombies
28 Glory Glory Buffy</pre>
===Idiomatic===
Or, at least semi-idiomatic / more-idiomatic, per comments above under the "Literal" implementation. Here, a "hashlist" structure is defined to allow retrieval either by indexed-list style (the database "rows") or by hashed-array-of-lists style (the database "index"), where the hash is maintained upon insert so that a later "hashjoin" operation becomes just a "join" operation. (Note that storage/performance issues are minimal, at least at this scale, as both the keys and rows in the hash are merely references, not copies.)
<syntaxhighlight lang="lua">local hashlist = {
new = function(self,key)
return setmetatable({key=key, hash={}, list={}}, {__index=self})
end,
insert = function(self,row)
self.list[#self.list+1] = row
if not self.hash[row[self.key]] then self.hash[row[self.key]]={} end
table.insert(self.hash[row[self.key]], row)
return self
end,
join = function(self,tabb)
local result = {}
for _,rowb in pairs(tabb.list) do
if (self.hash[rowb[tabb.key]]) then
for _,rowa in pairs(self.hash[rowb[tabb.key]]) do
result[#result+1] = { A=rowa, B=rowb }
end
end
end
return result
end
}
 
local function recA(age, name) return { Age=age, Name=name } end
tabA = hashlist:new("Name")
:insert(recA(27,"Jonah"))
:insert(recA(18,"Alan"))
:insert(recA(28,"Glory"))
:insert(recA(18,"Popeye"))
:insert(recA(28,"Alan"))
 
local function recB(character, nemesis) return { Character=character, Nemesis=nemesis } end
local tabB = hashlist:new("Character")
:insert(recB("Jonah","Whales"))
:insert(recB("Jonah","Spiders"))
:insert(recB("Alan","Ghosts"))
:insert(recB("Alan","Zombies"))
:insert(recB("Glory","Buffy"))
 
for _,row in pairs(tabA:join(tabB)) do
print(row.A.Age, row.A.Name, row.B.Character, row.B.Nemesis)
end
print("or vice versa:")
for _,row in pairs(tabB:join(tabA)) do
print(row.B.Age, row.B.Name, row.A.Character, row.A.Nemesis)
end</syntaxhighlight>
{{out}}
<pre>27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
28 Alan Alan Ghosts
18 Alan Alan Zombies
28 Alan Alan Zombies
28 Glory Glory Buffy
or vice versa:
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
18 Alan Alan Zombies
28 Glory Glory Buffy
28 Alan Alan Ghosts
28 Alan Alan Zombies</pre>
 
=={{header|LFE}}==
<langsyntaxhighlight lang="lisp">
(defun hash (column table)
(lists:foldl
Line 1,147 ⟶ 1,974:
(lc ((<- s (get-hash col-2 hashed)))
(merge r s)))))
</syntaxhighlight>
</lang>
 
Table definitions in the LFE REPL:
<langsyntaxhighlight lang="lisp">
> (set ss '((#(age 27) #(name "Jonah"))
(#(age 18) #(name "Alan"))
Line 1,162 ⟶ 1,989:
(#(nemesis "Zombies") #(name "Alan"))
(#(nemesis "Buffy") #(name "Glory"))))
</syntaxhighlight>
</lang>
 
Output in LFE REPL:
<langsyntaxhighlight lang="lisp">
> (hash-join ss 'name rs 'name)
(((#(age 27) #(name "Jonah") #(nemesis "Whales")))
Line 1,174 ⟶ 2,001:
(#(age 28) #(name "Alan") #(nemesis "Zombies")))
((#(age 28) #(name "Glory") #(nemesis "Buffy"))))
</syntaxhighlight>
</lang>
 
=={{header|M2000 Interpreter}}==
<syntaxhighlight lang="m2000 interpreter">
Module HashJoin {
\\ normally we define variables when we put values to names
\\ so we can remove these two lines
Def Name$, Nemesis$
Def Long m, mc, items_size, A
\\ Lets make a container which use keys with hash function
Inventory A
\\ A now is a pointer to an Inventory, with Len(A)=0
\\ Print Type$(A)="Inventory"
\\ empty stack. We use current stack to place data
Flush
\Input B
data "Jonah", "Whales"
data "Jonah", "Spiders"
data "Alan", "Ghosts"
data "Alan", "Zombies"
data "Glory", "Buffy"
\\ Keys are unique, This is the HASH PHASE
While not empty {
Read Name$, Nemesis$
If Exist(A, Name$) Then {
m=Eval(A) ' get a pointer to array
Stack m {Data Nemesis$}
} Else Append A, Name$:=Stack:=Nemesis$ ' a stack object with one item
}
\\ Input A, this is the Long Table
data 27, "Jonah"
data 18, "Alan"
data 28, "Glory"
data 18, "Popeye"
data 28, "Alan"
\\ This is the JOIN PHASE
items_size=stack.size/2
\\ using items_size we can append data (using data) to stack
\\ $(0) is the default handler for columns.
\\ Letters justify to left, numbers to right.
\\ Letters can use more columns, and maybe wrap to more lines.
Print $(0), "Output during join"
Print "A.Age", "A.Name","B.Character", "B.Nemesis"
While items_size>0 {
Read Age, Name$
If exist(A, Name$) Then {
m=Eval(A) ' extract a pointer, this is for a stack object
mc=Each(m) ' make an iterator
While mc {
\\ we use $(1) for left justify numbers too
\\ normal StackItem$(stackobject) return top only
\\ we have to use StackItem$(stackobject, 3) to get 3rd
\\ or StackItem(stackobject, 3) if it is numeric.
\\ but here mc is iterator, and place the cursor value to it
Print $(1), Age, Name$,Name$, StackItem$(mc)
\\ so now we place at the end of current stack the same output
Data Age, Name$,Name$, StackItem$(mc)
}
}
items_size--
}
\\ split rem line after : to use second way
rem : goto secondway
Print $(0), "Output after join"
Print "A.Age", "A.Name","B.Character", "B.Nemesis"
While not Empty {
Print $(1), Number, Letter$, Letter$, Letter$
}
Exit
secondway:
Print $(0), "Output after join using format$()"
Print Format$("{0:5} {1:10} {2:10} {3:20}","A.Age", "A.Name","B.Character", "B.Nemesis")
While not Empty {
Print format$("{0::5} {1:10} {2:10} {3:20}", Number, Letter$, Letter$, Letter$)
}
}
HashJoin
</syntaxhighlight>
{{out}}
<pre >
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
18 Alan Alan Zombies
28 Glory Glory Buffy
28 Alan Alan Ghosts
28 Alan Alan Zombies
 
</pre >
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
Line 1,181 ⟶ 2,101:
 
Updated version is now able to join wider tables by giving the index. The smaller table is hashed but this might result in different column ordering. Uses Associations introduced in Mathematica Version 10
<langsyntaxhighlight lang="mathematica">hashJoin[table1_List,table1colindex_Integer,table2_List,table2colindex_Integer]:=Module[{h,f,t1,t2,tmp},
t1=If[table1colindex != 1,table1[[All,Prepend[Delete[Range@Length@table1[[1]],table1colindex],table1colindex]]],table1];
t2=If[table2colindex != 1, table2[[All,Prepend[Delete[Range@Length@table2[[1]],table2colindex],table2colindex]]],table2];
Line 1,190 ⟶ 2,110:
Partition[Flatten[Map[f,{#[[2;;]],h[#[[1]]]}&/@t2
]],Length[t1[[1]]]+Length[t2[[1]]]-1]
];</langsyntaxhighlight>
Sample run:
<pre>
Line 1,249 ⟶ 2,169:
28 4 Glory 35 Buffy
</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import strformat, tables
 
type
Data1 = tuple[value: int; key: string]
Data2 = tuple[key: string; value: string]
 
proc `$`(d: Data1 | Data2): string = &"({d[0]}, {d[1]})"
 
iterator hashJoin(table1: openArray[Data1]; table2: openArray[Data2]): tuple[a: Data1; b: Data2] =
# Hash phase.
var h: Table[string, seq[Data1]]
for s in table1:
h.mgetOrPut(s.key, @[]).add(s)
# Join phase.
for r in table2:
for s in h[r.key]:
yield (s, r)
 
 
let table1 = [(27, "Jonah"),
(18, "Alan"),
(28, "Glory"),
(18, "Popeye"),
(28, "Alan")]
 
let table2 = [("Jonah", "Whales"),
("Jonah", "Spiders"),
("Alan", "Ghosts"),
("Alan", "Zombies"),
("Glory", "Buffy")]
 
for row in hashJoin(table1, table2):
echo row.a, " ", row.b</syntaxhighlight>
 
{{out}}
<pre>(27, Jonah) (Jonah, Whales)
(27, Jonah) (Jonah, Spiders)
(18, Alan) (Alan, Ghosts)
(28, Alan) (Alan, Ghosts)
(18, Alan) (Alan, Zombies)
(28, Alan) (Alan, Zombies)
(28, Glory) (Glory, Buffy)</pre>
 
=={{header|Oberon-2}}==
Works with oo2c version 2
<langsyntaxhighlight lang="oberon2">
MODULE HashJoin;
IMPORT
Line 1,347 ⟶ 2,311:
DoJoinPhase(tableB,dict);
END HashJoin.
</syntaxhighlight>
</lang>
Output:
<pre>
Line 1,361 ⟶ 2,325:
=={{header|OCaml}}==
This exploits the fact that Hashtbl implements a hash table that can store multiple values for a key, for an especially simple solution:
<langsyntaxhighlight lang="ocaml">let hash_join table1 f1 table2 f2 =
let h = Hashtbl.create 42 in
(* hash phase *)
Line 1,368 ⟶ 2,332:
(* join phase *)
List.concat (List.map (fun r ->
List.map (fun s -> s, r) (Hashtbl.find_all h (f2 r))) table2)</langsyntaxhighlight>
Sample run:
<pre>
Line 1,390 ⟶ 2,354:
 
=={{header|Perl}}==
<langsyntaxhighlight lang="perl">use Data::Dumper qw(Dumper);
 
sub hashJoin {
Line 1,419 ⟶ 2,383:
foreach my $row (hashJoin(\@table1, 1, \@table2, 0)) {
print Dumper($row), "\n";
}</langsyntaxhighlight>
{{out}}
<pre>
Line 1,431 ⟶ 2,395:
</pre>
 
=={{header|Perl 6Phix}}==
Phix dictionary keys must be unique, but storing/extending a sequence is no trouble, and in fact simplifies the scan phase.
 
<!--<syntaxhighlight lang="phix">(phixonline)-->
The <tt>.classify</tt> method returns a multimap represented as a <tt>Hash</tt> whose values are <tt>Array</tt>s.
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
 
<span style="color: #008080;">constant</span> <span style="color: #000000;">A</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">27</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Jonah"</span><span style="color: #0000FF;">},</span>
{{works with|Rakudo|2016.07}}
<span style="color: #0000FF;">{</span><span style="color: #000000;">18</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Alan"</span><span style="color: #0000FF;">},</span>
<lang perl6>sub hash-join(@a, &a, @b, &b) {
<span style="color: #0000FF;">{</span><span style="color: #000000;">28</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Glory"</span><span style="color: #0000FF;">},</span>
my %hash := @b.classify(&b);
<span style="color: #0000FF;">{</span><span style="color: #000000;">18</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Popeye"</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">28</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Alan"</span><span style="color: #0000FF;">}},</span>
@a.map: -> $a {
<span style="color: #000000;">B</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #008000;">"Jonah"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Whales"</span><span style="color: #0000FF;">},</span>
|(%hash{a $a} // next).map: -> $b { [$a, $b] }
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Jonah"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Spiders"</span><span style="color: #0000FF;">},</span>
}
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Alan"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"Ghosts"</span><span style="color: #0000FF;">},</span>
}</lang>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Alan"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"Zombies"</span><span style="color: #0000FF;">},</span>
 
<span style="color: #0000FF;">{</span><span style="color: #008000;">"Glory"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Buffy"</span><span style="color: #0000FF;">}},</span>
Testing:
<span style="color: #000000;">jA</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">,</span>
 
<span style="color: #000000;">jB</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span>
<lang perl6>my @A =
<span style="color: #000000;">MB</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">()</span>
[27, "Jonah"],
<span style="color: #004080;">sequence</span> <span style="color: #000000;">C</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
[18, "Alan"],
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">B</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
[28, "Glory"],
<span style="color: #004080;">object</span> <span style="color: #000000;">key</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">B</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">jB</span><span style="color: #0000FF;">]</span>
[18, "Popeye"],
<span style="color: #004080;">object</span> <span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">MB</span><span style="color: #0000FF;">)</span>
[28, "Alan"],
<span style="color: #008080;">if</span> <span style="color: #000000;">data</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
;
<span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">B</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]}</span>
 
<span style="color: #008080;">else</span>
my @B =
<span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">data</span><span style="color: #0000FF;">,</span><span style="color: #000000;">B</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
["Jonah", "Whales"],
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
["Jonah", "Spiders"],
<span style="color: #7060A8;">putd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">data</span><span style="color: #0000FF;">,</span><span style="color: #000000;">MB</span><span style="color: #0000FF;">)</span>
["Alan", "Ghosts"],
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
["Alan", "Zombies"],
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">A</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
["Glory", "Buffy"],
<span style="color: #004080;">object</span> <span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">A</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">jA</span><span style="color: #0000FF;">],</span><span style="color: #000000;">MB</span><span style="color: #0000FF;">)</span>
;
<span style="color: #008080;">if</span> <span style="color: #004080;">sequence</span><span style="color: #0000FF;">(</span><span style="color: #000000;">data</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
 
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">data</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
.say for hash-join @A, *[1], @B, *[0];</lang>
<span style="color: #000000;">C</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">C</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">A</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">data</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]})</span>
 
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">destroy_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">MB</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">pp</span><span style="color: #0000FF;">(</span><span style="color: #000000;">C</span><span style="color: #0000FF;">,{</span><span style="color: #004600;">pp_Nest</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">})</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
<pre>[[27 Jonah] [Jonah Whales]]
[[{{{27, "Jonah]"}, [{"Jonah", Spiders]]"Whales"}},
{{27, "Jonah"}, {"Jonah", "Spiders"}},
[[18 Alan] [Alan Ghosts]]
[[ {{18, "Alan]"}, [{"Alan", Zombies]]"Ghosts"}},
{{18, "Alan"}, {"Alan", "Zombies"}},
[[28 Glory] [Glory Buffy]]
{{28, "Glory"}, {"Glory", "Buffy"}},
[[28 Alan] [Alan Ghosts]]
[[ {{28, "Alan]"}, [{"Alan", Zombies]]</pre>"Ghosts"}},
{{28, "Alan"}, {"Alan", "Zombies"}}}
</pre>
 
=={{header|PHP}}==
<langsyntaxhighlight lang="php"><?php
function hashJoin($table1, $index1, $table2, $index2) {
// hash phase
Line 1,500 ⟶ 2,472:
foreach (hashJoin($table1, 1, $table2, 0) as $row)
print_r($row);
?></langsyntaxhighlight>
{{out}}
<pre>
Line 1,608 ⟶ 2,580:
 
)</pre>
 
=={{header|PicoLisp}}==
<syntaxhighlight lang="picolisp">(de A
(27 . Jonah)
(18 . Alan)
(28 . Glory)
(18 . Popeye)
(28 . Alan) )
 
(de B
(Jonah . Whales)
(Jonah . Spiders)
(Alan . Ghosts)
(Alan . Zombies)
(Glory . Buffy) )
 
(for X B
(let K (cons (char (hash (car X))) (car X))
(if (idx 'M K T)
(push (caar @) (cdr X))
(set (car K) (list (cdr X))) ) ) )
 
(for X A
(let? Y (car (idx 'M (cons (char (hash (cdr X))) (cdr X))))
(for Z (caar Y)
(println (car X) (cdr X) (cdr Y) Z) ) ) )</syntaxhighlight>
Output:
<pre>27 Jonah Jonah Spiders
27 Jonah Jonah Whales
18 Alan Alan Zombies
18 Alan Alan Ghosts
28 Glory Glory Buffy
28 Alan Alan Zombies
28 Alan Alan Ghosts</pre>
 
=={{header|plainTeX}}==
Works with any TeX engine.
<syntaxhighlight lang="tex">\newtoks\tabjoin
\def\quark{\quark}
\def\tabA{27:Jonah,18:Alan,28:Glory,18:Popeye,28:Alan}
\def\tabB{Jonah:Whales,Jonah:Spiders,Alan:Ghosts,Alan:Zombies,Glory:Buffy}
\def\mergejoin{\tabjoin{}\expandafter\mergejoini\tabA,\quark:\quark,}
\def\mergejoini#1:#2,{%
\ifx\quark#1\the\tabjoin
\else
\def\mergejoinii##1,#2:##2,{%
\ifx\quark##2\else
\tabjoin\expandafter{\the\tabjoin#1 : #2 : ##2\par}%
\expandafter\mergejoinii\expandafter,%
\fi
}%
\expandafter\mergejoinii\expandafter,\tabB,#2:\quark,%
\expandafter\mergejoini
\fi
}
\mergejoin
\bye</syntaxhighlight>
 
pdf or dvi output:
<pre>27 : Jonah : Whales
27 : Jonah : Spiders
18 : Alan : Ghosts
18 : Alan : Zombies
28 : Glory : Buffy
28 : Alan : Ghosts
28 : Alan : Zombies</pre>
 
=={{header|Prolog}}==
<syntaxhighlight lang="prolog">% Name/Age
person_age('Jonah', 27).
person_age('Alan', 18).
person_age('Glory', 28).
person_age('Popeye', 18).
person_age('Alan', 28).
 
% Character/Nemesis
character_nemisis('Jonah', 'Whales').
character_nemisis('Jonah', 'Spiders').
character_nemisis('Alan', 'Ghosts').
character_nemisis('Alan', 'Zombies').
character_nemisis('Glory', 'Buffy').
 
join_and_print :-
format('Age\tName\tCharacter\tNemisis\n\n'),
forall(
(person_age(Person, Age), character_nemisis(Person, Nemesis)),
format('~w\t~w\t~w\t\t~w\n', [Age, Person, Person, Nemesis])
).</syntaxhighlight>
{{out}}
<pre>
?- join_and_print.
Age Name Character Nemisis
 
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
18 Alan Alan Zombies
28 Glory Glory Buffy
28 Alan Alan Ghosts
28 Alan Alan Zombies
true.
</pre>
 
=={{header|PureBasic}}==
<syntaxhighlight lang="purebasic">Structure tabA
age.i
name.s
EndStructure
 
Structure tabB
char_name.s
nemesis.s
EndStructure
 
NewList listA.tabA()
NewList listB.tabB()
 
Macro SetListA(c_age, c_name)
AddElement(listA()) : listA()\age = c_age : listA()\name = c_name
EndMacro
 
Macro SetListB(c_char, c_nem)
AddElement(listB()) : listB()\char_name = c_char : listB()\nemesis = c_nem
EndMacro
 
SetListA(27, "Jonah") : SetListA(18, "Alan") : SetListA(28, "Glory")
SetListA(18, "Popeye") : SetListA(28, "Alan")
 
SetListB("Jonah", "Whales") : SetListB("Jonah", "Spiders")
SetListB("Alan", "Ghosts") : SetListB("Alan", "Zombies")
SetListB("Glory", "Buffy")
 
If OpenConsole("Hash_join")
ForEach listA()
PrintN("Input A = "+Str(listA()\age)+~"\t"+listA()\name)
Next
PrintN("")
ForEach listB()
PrintN("Input B = "+listB()\char_name+~"\t"+listB()\nemesis)
Next
PrintN(~"\nOutput\nA.Age\tA.Name\tB.Char.\tB.Nemesis")
ForEach listA()
ForEach listB()
If listA()\name = listB()\char_name
PrintN(Str(listA()\age)+~"\t"+listA()\name+~"\t"+
listB()\char_name+~"\t"+listB()\nemesis)
EndIf
Next
Next
Input()
EndIf</syntaxhighlight>
{{out}}
<pre>Input A = 27 Jonah
Input A = 18 Alan
Input A = 28 Glory
Input A = 18 Popeye
Input A = 28 Alan
 
Input B = Jonah Whales
Input B = Jonah Spiders
Input B = Alan Ghosts
Input B = Alan Zombies
Input B = Glory Buffy
 
Output
A.Age A.Name B.Char. B.Nemesis
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
18 Alan Alan Zombies
28 Glory Glory Buffy
28 Alan Alan Ghosts
28 Alan Alan Zombies</pre>
 
=={{header|Python}}==
<langsyntaxhighlight lang="python">from collections import defaultdict
 
def hashJoin(table1, index1, table2, index2):
Line 1,632 ⟶ 2,777:
 
for row in hashJoin(table1, 1, table2, 0):
print(row)</langsyntaxhighlight>
{{out}}
<pre>
Line 1,645 ⟶ 2,790:
 
=={{header|Racket}}==
<langsyntaxhighlight lang="racket">#lang racket
(struct A (age name))
(struct B (name nemesis))
Line 1,673 ⟶ 2,818:
(key (in-value (B-name b)))
(age (in-list (hash-ref name->ages# key))))
(AB key age (B-nemesis b)))</langsyntaxhighlight>
 
{{out}}
Line 1,683 ⟶ 2,828:
#(struct:AB "Alan" 28 "Zombies")
#(struct:AB "Glory" 28 "Buffy"))</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
 
The <tt>.classify</tt> method returns a multimap represented as a <tt>Hash</tt> whose values are <tt>Array</tt>s.
 
<syntaxhighlight lang="raku" line>sub hash-join(@a, &a, @b, &b) {
my %hash := @b.classify(&b);
@a.map: -> $a {
|(%hash{$a.&a} // next).map: -> $b { [$a, $b] }
}
}
 
my @A =
[27, "Jonah"],
[18, "Alan"],
[28, "Glory"],
[18, "Popeye"],
[28, "Alan"],
;
 
my @B =
["Jonah", "Whales"],
["Jonah", "Spiders"],
["Alan", "Ghosts"],
["Alan", "Zombies"],
["Glory", "Buffy"],
;
 
.say for hash-join @A, *[1], @B, *[0];</syntaxhighlight>
 
{{out}}
<pre>[[27 Jonah] [Jonah Whales]]
[[27 Jonah] [Jonah Spiders]]
[[18 Alan] [Alan Ghosts]]
[[18 Alan] [Alan Zombies]]
[[28 Glory] [Glory Buffy]]
[[28 Alan] [Alan Ghosts]]
[[28 Alan] [Alan Zombies]]</pre>
 
=={{header|REXX}}==
<langsyntaxhighlight lang="rexx">/*REXX program demonstrates the classic hash join algorithm for two relations. */
S. = ; R. =
S.1 = 27 'Jonah' ; R.1 = "Jonah Whales"
Line 1,715 ⟶ 2,900:
if nems=='' then iterate /*No nemesis? Skip. */
say pad right(age,3) pad center(name,20) pad center(nems,30) /*display an "S". */
end /*n*/ /*stick a fork in it, we're all done. */</langsyntaxhighlight>
'''output''' &nbsp; when using the in-code relations (data):
<pre>
Line 1,727 ⟶ 2,912:
 
=={{header|Ring}}==
<langsyntaxhighlight lang="ring">Table1 = [[27, "Jonah"], [18, "Alan"], [28, "Glory"], [18, "Popeye"], [28, "Alan"]]
Table2 = [["Jonah", "Whales"], ["Jonah", "Spiders"], ["Alan", "Ghosts"], ["Alan", "Zombies"], ["Glory", "Buffy"]]
hTable = []
Line 1,768 ⟶ 2,953:
next
ok
return r</langsyntaxhighlight>
{{out}}
<pre>
Line 1,784 ⟶ 2,969:
 
=={{header|Ruby}}==
<langsyntaxhighlight lang="ruby">def hashJoin(table1, index1, table2, index2)
# hash phase
h = table1.group_by {|s| s[index1]}
Line 1,805 ⟶ 2,990:
["Glory", "Buffy"]]
 
hashJoin(table1, 1, table2, 0).each { |row| p row }</langsyntaxhighlight>
 
{{out}}
Line 1,819 ⟶ 3,004:
 
=={{header|Run BASIC}}==
<langsyntaxhighlight Runbasiclang="runbasic">sqliteconnect #mem, ":memory:"
 
#mem execute("CREATE TABLE t_age(age,name)")
Line 1,843 ⟶ 3,028:
nemesis$ = #row nemesis$()
print age;" ";name$;" ";nemesis$
WEND</langsyntaxhighlight>Output:
<pre>27 Jonah Spiders
27 Jonah Whales
Line 1,854 ⟶ 3,039:
 
=={{header|Rust}}==
<syntaxhighlight lang="rust">use std::collections::HashMap;
<lang rust>
use std::collectionshash::HashMapHash;
 
// If you know one of the tables is smaller, it is best to make it the second parameter.
fn hash_join<A, B, K>(first: &[(K, A)], second: &[(K, B)]) -> Vec<(A, K, B)>
where
K: Hash + Eq + Copy,
A: Copy,
B: Copy,
{
let mut hash_map = HashMap::new();
 
fn main() {
let table_a = vec![
(27, "Jonah"), (18, "Alan"), (28, "Glory"),
(18, "Popeye"), (28, "Alan")
];
let table_b = vec![
("Jonah", "Whales"), ("Jonah", "Spiders"), ("Alan", "Ghosts"),
("Alan", "Zombies"), ("Glory", "Buffy")
];
// hash phase
for &(key, val_a) in second {
let mut h = HashMap::new();
// collect all values by their keys, appending new ones to each existing entry
for (i, a) in table_a.iter().enumerate() {
hhash_map.entry(a.1key).or_insertor_insert_with(vec![]Vec::new).push(ival_a);
}
 
let mut result = Vec::new();
// join phase
for b&(key, val_b) in table_bfirst {
forif ilet inSome(vals) h= hash_map.get(b.0).unwrap_or(&vec![]key) {
let atuples = table_avals.getiter(*i).unwrapmap(|&val_a| (val_b, key, val_a));
println!result.extend("{:?} {:?}", a, btuples);
}
}
 
result
}
 
</lang>
fn main() {
let table1 = [("Jonah", 27), ("Alan", 18), ("Glory", 28), ("Popeye", 18), ("Alan", 28)];
let table2 = [
("Jonah", "Whales"), ("Jonah", "Spiders"), ("Alan", "Ghosts"),
("Alan", "Zombies"), ("Glory", "Buffy")
];
let result = hash_join(&table1, &table2);
println!("Age | Character Name | Nemesis");
println!("----|----------------|--------");
for (age, name, nemesis) in result {
println!("{:<3} | {:^14} | {}", age, name, nemesis);
}
}</syntaxhighlight>
{{out}}
<pre>Age | Character Name | Nemesis
<pre>
----|----------------|--------
(27, "Jonah") ("Jonah", "Whales")
(27, "Jonah") ("| Jonah", "Spiders") | Whales
27 | Jonah | Spiders
(18, "Alan") ("Alan", "Ghosts")
(28,18 | "Alan") ("Alan", " | Ghosts")
(18, "Alan") ("| Alan", " | Zombies")
28 | Glory | Buffy
(28, "Alan") ("Alan", "Zombies")
28 | Alan | Ghosts
(28, "Glory") ("Glory", "Buffy")
28 | Alan | Zombies</pre>
</pre>
 
=={{header|Scala}}==
<langsyntaxhighlight Scalalang="scala">def join[Type](left: Seq[Seq[Type]], right: Seq[Seq[Type]]) = {
val hash = right.groupBy(_.head) withDefaultValue Seq()
left.flatMap(cols => hash(cols.last).map(cols ++ _.tail))
Line 1,910 ⟶ 3,112:
List("Glory", "Buffy"))
 
println(join(table1, table2) mkString "\n")</langsyntaxhighlight>
{{out}}
<pre>List(27, Jonah, Whales)
Line 1,922 ⟶ 3,124:
=={{header|Scheme}}==
{{works with|Gauche Scheme}}
<langsyntaxhighlight Schemelang="scheme">(use srfi-42)
 
(define ages '((27 Jonah) (18 Alan) (28 Glory) (18 Popeye) (28 Alan)))
Line 1,942 ⟶ 3,144:
(print (list (list age name)
person)))
</syntaxhighlight>
</lang>
{{output}}
<pre>
Line 1,955 ⟶ 3,157:
 
=={{header|Sidef}}==
<langsyntaxhighlight lang="ruby">func hashJoin(table1, index1, table2, index2) {
var a = []
var h = Hash()
Line 1,984 ⟶ 3,186:
["Glory", "Buffy"]]
 
hashJoin(t1, 1, t2, 0).each { .say }</langsyntaxhighlight>
{{out}}
<pre>[[27, 'Jonah'], ['Jonah', 'Whales']]
Line 1,995 ⟶ 3,197:
 
=={{header|SQL}}==
{{works with|oracle}}
Setting up the data is a bit verbose:
<syntaxhighlight lang="sql">-- setting up the test data
<lang sql>create table people (age decimal(3), name varchar(16));
insert into people (age, name) values (27, 'Jonah');
insert into people (age, name) values (18, 'Alan');
insert into people (age, name) values (28, 'Glory');
insert into people (age, name) values (18, 'Popeye');
insert into people (age, name) values (28, 'Alan');
 
create table nemesisespeople (nameage varcharnumber(163), nemesisname varcharvarchar2(1630));
insert into nemesisespeople (nameage, nemesis) values ('Jonah', 'Whales'name);
select 27, 'Jonah' from dual union all
insert into nemesises (name, nemesis) values ('Jonah', 'Spiders');
select 18, 'Alan' from dual union all
insert into nemesises (name, nemesis) values ('Alan', 'Ghosts');
select 28, 'Glory' from dual union all
insert into nemesises (name, nemesis) values ('Alan', 'Zombies');
select 18, 'Popeye' from dual union all
insert into nemesises (name, nemesis) values ('Glory', 'Buffy');</lang>
select 28, 'Alan' from dual
;
 
create table nemesises (name varchar2(30), nemesis varchar2(30));
Doing the join is concise. But we don't actually have control over how the join is implemented, so this might not actually be a hash join...
<langinsert sql>select * from people p joininto nemesises n on p.(name, =nemesis) n.name</lang>
select 'Jonah', 'Whales' from dual union all
select 'Jonah', 'Spiders' from dual union all
select 'Alan' , 'Ghosts' from dual union all
select 'Alan' , 'Zombies' from dual union all
select 'Glory', 'Buffy' from dual
;</syntaxhighlight>
 
Doing the join is trivial. Normally we would let the optimizer select the join method. However, to force a hash join, we can use an optimizer hint, USE_HASH.
<syntaxhighlight lang="sql">select /*+ use_hash */ * from people join nemesises using(name);</syntaxhighlight>
 
{{out}}
<pre> AGE NAME NAME NEMESIS
---------- ---------------- ---------------- ----------------
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
28 Alan Alan Ghosts
18 Alan Alan Ghosts
28 Alan Alan Zombies
18 Alan Alan Zombies
28 Glory Glory Buffy</pre>
 
=={{header|Swift}}==
 
{{trans|Rust}}
 
<syntaxhighlight lang="swift">func hashJoin<A, B, K: Hashable>(_ first: [(K, A)], _ second: [(K, B)]) -> [(A, K, B)] {
var map = [K: [B]]()
 
for (key, val) in second {
map[key, default: []].append(val)
}
 
var res = [(A, K, B)]()
 
for (key, val) in first {
guard let vals = map[key] else {
continue
}
 
res += vals.map({ (val, key, $0) })
}
 
return res
}
 
let t1 = [
("Jonah", 27),
("Alan", 18),
("Glory", 28),
("Popeye", 18),
("Alan", 28)
]
 
let t2 = [
("Jonah", "Whales"),
("Jonah", "Spiders"),
("Alan", "Ghosts"),
("Alan", "Zombies"),
("Glory", "Buffy")
]
 
print("Age | Character Name | Nemesis")
print("----|----------------|--------")
 
for (age, name, nemesis) in hashJoin(t1, t2) {
print("\(age) | \(name) | \(nemesis)")
}</syntaxhighlight>
 
{{out}}
 
<pre>Age | Character Name | Nemesis
----|----------------|--------
27 | Jonah | Whales
27 | Jonah | Spiders
18 | Alan | Ghosts
18 | Alan | Zombies
28 | Glory | Buffy
28 | Alan | Ghosts
28 | Alan | Zombies</pre>
 
=={{header|Tcl}}==
Tcl uses hash tables to implement both its associative arrays and its dictionaries.
<langsyntaxhighlight lang="tcl">package require Tcl 8.6
# Only for lmap, which can be replaced with foreach
 
Line 2,064 ⟶ 3,331:
foreach row $joined {
puts $row
}</langsyntaxhighlight>
{{out}}
<pre>
Line 2,080 ⟶ 3,347:
Generic hash join. Arguments <code>left-key</code> and <code>right-key</code> are functions applied to the elements of the <code>left</code> and <code>right</code> sequences to retrieve the join key.
 
<langsyntaxhighlight lang="txrlisp">(defvar age-name '((27 Jonah)
(18 Alan)
(28 Glory)
Line 2,098 ⟶ 3,365:
^(,l-entry ,r-entry)))))
 
(format t "~s\n" [hash-join age-name second nemesis-name first])</langsyntaxhighlight>
 
{{out}}
Line 2,106 ⟶ 3,373:
 
=={{header|VBScript}}==
<syntaxhighlight lang="vb">
<lang vb>
Dim t_age(4,1)
t_age(0,0) = 27 : t_age(0,1) = "Jonah"
Line 2,137 ⟶ 3,404:
Next
End Sub
</syntaxhighlight>
</lang>
 
{{Out}}
Line 2,152 ⟶ 3,419:
=={{header|Visual FoxPro}}==
Hashing using the common key (name) gives ambiguous results as the name column is not unique in either table (a unique key could be formed by using the age and name columns) . This implementation forces a unique key on the people table.
<langsyntaxhighlight lang="vfp">
LOCAL i As Integer, n As Integer
CLOSE DATABASES ALL
Line 2,207 ⟶ 3,474:
RETURN a[1]
ENDFUNC
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 2,216 ⟶ 3,483:
18 Alan Zombies
28 Glory Buffy
</pre>
 
=={{header|Wren}}==
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./fmt" for Fmt
 
class A {
construct new(age, name) {
_age = age
_name = name
}
 
age { _age }
name { _name }
}
 
class B {
construct new(character, nemesis) {
_character = character
_nemesis = nemesis
}
 
character { _character }
nemesis { _nemesis }
}
 
var tableA = [
A.new(27, "Jonah"), A.new(18, "Alan"), A.new(28, "Glory"),
A.new(18, "Popeye"), A.new(28, "Alan")
]
var tableB = [
B.new("Jonah", "Whales"), B.new("Jonah", "Spiders"), B.new("Alan", "Ghosts"),
B.new("Alan", "Zombies"), B.new("Glory", "Buffy")
]
var h = {}
var i = 0
for (a in tableA) {
var n = h[a.name]
if (n) {
n.add(i)
} else {
h[a.name] = [i]
}
i = i + 1
}
 
System.print("Age Name Character Nemesis")
System.print("--- ----- --------- -------")
for (b in tableB) {
var c = h[b.character]
if (c) {
for (i in c) {
var t = tableA[i]
Fmt.print("$3d $-5s $-9s $s", t.age, t.name, b.character, b.nemesis)
}
}
}</syntaxhighlight>
 
{{out}}
<pre>
Age Name Character Nemesis
--- ----- --------- -------
27 Jonah Jonah Whales
27 Jonah Jonah Spiders
18 Alan Alan Ghosts
28 Alan Alan Ghosts
18 Alan Alan Zombies
28 Alan Alan Zombies
28 Glory Glory Buffy
</pre>
 
=={{header|zkl}}==
Join two tables by hashing on the common key (name). The resulting join is the intersection of the two tables.
<langsyntaxhighlight lang="zkl">ageName:=T(27,"Jonah", 18,"Alan", 28,"Glory", 18,"Popeye", 28,"Alan");
nameNemesis:=T("Jonah","Whales", "Jonah","Spiders", "Alan","Ghosts",
"Alan","Zombies", "Glory","Buffy");
Line 2,243 ⟶ 3,579:
val:=d[name]; if (not val[1])return(Void.Skip);
String(name,":",d[name][1].concat(","));
})</langsyntaxhighlight>
zkl Dictionaries only have one key
<pre>
2,122

edits