Task: Hash join


C++: #include <iostream>
#include <string>
#include <vector>
#include <unordered_map>

using tab_t = std::vector<std::vector<std::string>>;
tab_t tab1 {

  {"27", "Jonah"}
, {"18", "Alan"}
, {"28", "Glory"}
, {"18", "Popeye"}
, {"28", "Alan"}
};

tab_t tab2 {

  {"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;
  
  for(size_t i = 0; i < a.size(); ++i) {
    hashmap.insert(std::make_pair(a[i][columna], i));
  }
  
  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;
}


C#: using System;
using System.Collections.Generic;
using System.Linq;

namespace HashJoin
{
    public class AgeName
    {
        public AgeName(byte age, string name)
        {
            Age = age;
            Name = name;
        }
        public byte Age { get; private set; }
        public string Name { get; private set; }
    }

    public class NameNemesis
    {
        public NameNemesis(string name, string nemesis)
        {
            Name = name;
            Nemesis = nemesis;
        }
        public string Name { get; private set; }
        public string Nemesis { get; private set; }
    }

    public class DataContext
    {
        public DataContext()
        {
            AgeName = new List<AgeName>();
            NameNemesis = new List<NameNemesis>();
        }
        public List<AgeName> AgeName { get; set; }
        public List<NameNemesis> NameNemesis { get; set; }
    }

    public class AgeNameNemesis
    {
        public AgeNameNemesis(byte age, string name, string nemesis)
        {
            Age = age;
            Name = name;
            Nemesis = nemesis;
        }
        public byte Age { get; private set; }
        public string Name { get; private set; }
        public string Nemesis { get; private set; }
    }

    class Program
    {
        public static void Main()
        {
            var data = GetData();
            var result = ExecuteHashJoin(data);
            WriteResultToConsole(result);
        }

        private static void WriteResultToConsole(List<AgeNameNemesis> result)
        {
            result.ForEach(ageNameNemesis => Console.WriteLine("Age: {0}, Name: {1}, Nemesis: {2}",
                ageNameNemesis.Age, ageNameNemesis.Name, ageNameNemesis.Nemesis));
        }

        private static List<AgeNameNemesis> ExecuteHashJoin(DataContext data)
        {
            return (data.AgeName.Join(data.NameNemesis, 
                ageName => ageName.Name, nameNemesis => nameNemesis.Name,
                (ageName, nameNemesis) => new AgeNameNemesis(ageName.Age, ageName.Name, nameNemesis.Nemesis)))
                .ToList();
        }

        private static DataContext GetData()
        {
            var context = new DataContext();

            context.AgeName.AddRange(new [] {
                    new AgeName(27, "Jonah"), 
                    new AgeName(18, "Alan"), 
                    new AgeName(28, "Glory"), 
                    new AgeName(18, "Popeye"), 
                    new AgeName(28, "Alan")
                });

            context.NameNemesis.AddRange(new[]
            {
                new NameNemesis("Jonah", "Whales"),
                new NameNemesis("Jonah", "Spiders"),
                new NameNemesis("Alan", "Ghosts"),
                new NameNemesis("Alan", "Zombies"),
                new NameNemesis("Glory", "Buffy")
            });

            return context;
        }
    }
}