function zipObjects(arrays, joinField, rename) {
  if (!arrays.length) {
    return [];
  }
  return arrays[0].map((sampleObj, i) => {
    const renamedObjs = arrays.map(arr => arr[i]).map((obj, j) => {
      // make sure joinField is matched for each object in each sub-array
      assert(sampleObj[joinField] === obj[joinField]);
      return Object.keys(obj).reduce((acc, key) => {
        const renamedKey = rename[j][key] || key;
        acc[renamedKey] = obj[key];
        return acc;
      }, {});
    });
    // todo: assert there are no duplicated keys
    return Object.assign.apply(null, renamedObjs);
  });
}
merge objects from with the same `joinField`, rename duplicated fields
@param {Array<Array<Object>>} arrays
@param {String} joinField field to hold consistant among joined objects
@param {Array<Object>} rename array of mapping from original key to new key
@returns {Array<Object>} joined array of objects
@example
const arrays = [
  [{name: 'alice', score: 99}, {name: 'bob', score: 80}],
  [{name: 'alice', score: 'A'}, {name: 'bob', score: 'B'}]
];
const rename = [{}, {score: 'gradeScore'}];
const zippped = zipObjects(arrays, 'name', rename);
// zipped = [{name: 'alice', score: 99, gradeScore: 'A'}, {name: 'bob', score: 80, gradeScore: 'B'}]