Вы можете разделить дорожки на более мелкие части, такие как свойства и индексы, и построить новый объект, соблюдая следующий ключ и решить, нужно ли брать массив или объект.
function setValue(target, keys, value) {
const
isWrapped = s => s.startsWith('[') && s.endsWith(']'),
unwrap = s => isWrapped(s) ? s.slice(1, -1) : s,
path = keys.match(/\[[^\]+]\]|[^\.\[\]]+/g),
last = path.pop();
path
.reduce((o, k, i, { [i + 1]: next = last }) =>
o[unwrap(k)] = o[unwrap(k)] || (isWrapped(next) ? [] : {}), target)
[unwrap(last)] = value;
}
var keys = ['a.b', 'a.c.d', 'a.e', 'h[0]'],
values = [10, 20, {}, 40],
i,
l = keys.length,
result = {};
for (i = 0; i < l; i++) setValue(result, keys[i], values[i]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }