require 'set'
f = -> a {
h = a.map {|s| s.split('=')}.flatten.uniq.map.with_index.to_h
a.each_with_object([]) {|s, acc|
x, xs, y, ys = s.split('=').map {|k| [h[k], acc.find {|b| b.include? h[k]}]}.flatten(1)
if xs && ys then xs.merge(acc.delete ys)
elsif xs then xs << y
elsif ys then ys << x
else acc << SortedSet[x, y]
end
}.sort_by(&:first).map {|a| a.map &h.invert.method(:[]) }
}
a = ["a1=a2", "b1=b2", "b3=b2", "c1=c2", "e1=e2",
"a3=a4", "c3=c4", "e1=e3", "a2=a4", "c3=c1",
"b3=a4", "c2=d1", "a4=a5", "d2=c1", "b4=b3", "d3=c3"]
p f.(a)
cmVxdWlyZSAnc2V0JwpmID0gLT4gYSB7CiAgaCA9IGEubWFwIHt8c3wgcy5zcGxpdCgnPScpfS5mbGF0dGVuLnVuaXEubWFwLndpdGhfaW5kZXgudG9faAogIGEuZWFjaF93aXRoX29iamVjdChbXSkge3xzLCBhY2N8CiAgICB4LCB4cywgeSwgeXMgPSBzLnNwbGl0KCc9JykubWFwIHt8a3wgW2hba10sIGFjYy5maW5kIHt8YnwgYi5pbmNsdWRlPyBoW2tdfV19LmZsYXR0ZW4oMSkKICAgIGlmIHhzICYmIHlzIHRoZW4geHMubWVyZ2UoYWNjLmRlbGV0ZSB5cykKICAgIGVsc2lmIHhzIHRoZW4geHMgPDwgeQogICAgZWxzaWYgeXMgdGhlbiB5cyA8PCB4CiAgICBlbHNlIGFjYyA8PCBTb3J0ZWRTZXRbeCwgeV0KICAgIGVuZAogIH0uc29ydF9ieSgmOmZpcnN0KS5tYXAge3xhfCBhLm1hcCAmaC5pbnZlcnQubWV0aG9kKDpbXSkgfQp9CmEgPSBbImExPWEyIiwgImIxPWIyIiwgImIzPWIyIiwgImMxPWMyIiwgImUxPWUyIiwKImEzPWE0IiwgImMzPWM0IiwgImUxPWUzIiwgImEyPWE0IiwgImMzPWMxIiwKImIzPWE0IiwgImMyPWQxIiwgImE0PWE1IiwgImQyPWMxIiwgImI0PWIzIiwgImQzPWMzIl0KcCBmLihhKQo=
[["a1", "a2", "b1", "b2", "b3", "a3", "a4", "a5", "b4"], ["c1", "c2", "c3", "c4", "d1", "d2", "d3"], ["e1", "e2", "e3"]]