September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -0,0 +1,61 @@
# 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)
}

View file

@ -1,11 +1,13 @@
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 lambda(a, x)
on |λ|(a, x)
set strID to keyValue(x, jB)
set maybeInstances to keyValue(a, strID)
@ -14,32 +16,32 @@ on hashJoin(tblA, tblB, strJoin)
else
updatedRecord(a, strID, [x])
end if
end lambda
end |λ|
end script
set M to foldl(instanceOfjB, {name:"multiMap"}, tblB)
script joins
on lambda(a, x)
on |λ|(a, x)
set matches to keyValue(M, keyValue(x, jA))
if matches is not missing value then
script concat
on lambda(row)
on |λ|(row)
x & row
end lambda
end |λ|
end script
a & map(concat, matches)
else
a
end if
end lambda
end |λ|
end script
foldl(joins, {}, tblA)
end hashJoin
-- TEST
-- TEST ----------------------------------------------------------------------
on run
set lstA to [¬
{age:27, |name|:"Jonah"}, ¬
@ -60,12 +62,13 @@ on run
end run
-- RECORD PRIMITIVES
-- 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
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
@ -82,7 +85,7 @@ on updatedRecord(rec, strKey, varValue)
end updatedRecord
-- GENERIC PRIMITIVES
-- GENERIC FUNCTIONS ---------------------------------------------------------
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
@ -90,7 +93,7 @@ on foldl(f, startValue, xs)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to lambda(v, item i of xs, i, xs)
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
@ -102,20 +105,12 @@ on map(f, xs)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to lambda(item i of xs, i, xs)
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- 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
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
@ -123,7 +118,15 @@ on mReturn(f)
f
else
script
property lambda : f
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

View file

@ -0,0 +1,8 @@
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

View file

@ -0,0 +1,19 @@
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

View file

@ -0,0 +1,39 @@
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}")
}
}

View file

@ -0,0 +1,34 @@
constant A = {{27,"Jonah"},
{18,"Alan"},
{28,"Glory"},
{18,"Popeye"},
{28,"Alan"}},
B = {{"Jonah","Whales"},
{"Jonah","Spiders"},
{"Alan", "Ghosts"},
{"Alan", "Zombies"},
{"Glory","Buffy"}},
jA = 2,
jB = 1,
MB = new_dict()
sequence C = {}
for i=1 to length(B) do
object key = B[i][jB]
object data = getd(key,MB)
if data=0 then
data = {B[i]}
else
data = append(data,B[i])
end if
putd(key,data,MB)
end for
for i=1 to length(A) do
object data = getd(A[i][jA],MB)
if sequence(data) then
for j=1 to length(data) do
C = append(C,{A[i],data[j]})
end for
end if
end for
destroy_dict(MB)
pp(C,{pp_Nest,1})

View file

@ -0,0 +1,20 @@
\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

View file

@ -0,0 +1,47 @@
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

View file

@ -12,13 +12,15 @@ fn main() {
// hash phase
let mut h = HashMap::new();
for (i, a) in table_a.iter().enumerate() {
h.entry(a.1).or_insert(vec![]).push(i);
h.entry(a.1).or_insert_with(Vec::new).push(i);
}
// join phase
for b in table_b {
for i in h.get(b.0).unwrap_or(&vec![]) {
let a = table_a.get(*i).unwrap();
println!("{:?} {:?}", a, b);
if let Some(vals) = h.get(b.0) {
for &val in vals {
let a = table_a.get(val).unwrap();
println!("{:?} {:?}", a, b);
}
}
}
}

View file

@ -0,0 +1,24 @@
ageName:=T(27,"Jonah", 18,"Alan", 28,"Glory", 18,"Popeye", 28,"Alan");
nameNemesis:=T("Jonah","Whales", "Jonah","Spiders", "Alan","Ghosts",
"Alan","Zombies", "Glory","Buffy");
fcn addAN(age,name,d){ // keys are names, values are ( (age,...),() )
if (r:=d.find(name)) d[name] = T(r[0].append(age),r[1]);
else d.add(name,T(T(age),T));
d // return d so pump will use that as result for assignment
}
fcn addNN(name,nemesis,d){ // values-->( (age,age...), (nemesis,...) )
if (r:=d.find(name)){
ages,nemesises := r;
d[name] = T(ages,nemesises.append(nemesis));
}
}
// Void.Read --> take existing i, read next one, pass both to next function
var d=ageName.pump(Void,Void.Read,T(addAN,Dictionary()));
nameNemesis.pump(Void,Void.Read,T(addNN,d));
d.println(); // the union of the two tables
d.keys.sort().pump(Console.println,'wrap(name){ //pretty print the join
val:=d[name]; if (not val[1])return(Void.Skip);
String(name,":",d[name][1].concat(","));
})