Javascript - 将值映射到键(反向对象映射)

时间:2017-08-17 06:43:05

标签: javascript ecmascript-6

我想要反转对象的映射(可能有重复的值)。例如:

const city2country = {
    'Amsterdam': 'Netherlands',
    'Rotterdam': 'Netherlands',
    'Paris': 'France'
};

reverseMapping(city2country)应输出:

{
    'Netherlands': ['Amsterdam', 'Rotterdam'],
    'France': ['Paris']
}

我想出了以下天真的解决方案:

const reverseMapping = (obj) => {
    const reversed = {};
    Object.keys(obj).forEach((key) => {
        reversed[obj[key]] = reversed[obj[key]] || [];
        reversed[obj[key]].push(key);
    });
    return reversed;
};

但我很确定有一个更整洁,更短的方式,最好是原型,所以我可以这样做:

const country2cities = city2country.reverse();

感谢。

6 个答案:

答案 0 :(得分:6)

您可以使用Object.assign,同时尊重插入值的给定数组。



const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
const reverseMapping = o => Object.keys(o).reduce((r, k) =>
        Object.assign(r, { [o[k]]: (r[o[k]] || []).concat(k) }), {})

console.log(reverseMapping(city2country));




答案 1 :(得分:2)

JavaScript中没有这样的内置函数。您的代码看起来很好,但鉴于此处有太多边缘情况可能出错,我建议使用invertBy from lodash,它完全按照您的描述进行操作。

实施例

var object = { 'a': 1, 'b': 2, 'c': 1 };

_.invertBy(object);
// => { '1': ['a', 'c'], '2': ['b'] }

答案 2 :(得分:0)

你可以使用这样的东西来抢先重复:

function removeDuplicates(arr, key) {
   if (!(arr instanceof Array) || key && typeof key !== 'string') {
    return false;
   }

  if (key && typeof key === 'string') {
    return arr.filter((obj, index, arr) => {
        return arr.map(mapObj => mapObj[key]).indexOf(obj[key]) === index;
    });

  } else {
    return arr.filter(function(item, index, arr) {
        return arr.indexOf(item) == index;
    });
  }
} 

然后用它来反转:

function reverseMapping(obj){
  var ret = {};
  for(var key in obj){
  ret[obj[key]] = key;
  }
    return ret;
  }

答案 3 :(得分:0)

您可以使用reduce来保存声明行reduce

在使用&&之前,滥用map[object[key]]检查是否先定义Array.concat

它更短,但更简单吗?可能不是,但有点乐趣;)

const reverseMapping = (object) => 
    Object.keys(object).reduce((map, key) => {
        map[object[key]] = map[object[key]] && map[object[key]].concat(key) || [key]
     return map;
    }, {});

答案 4 :(得分:0)

您可以尝试从当前对象获取值数组和键数组,并设置一个新对象来保存结果。然后,循环遍历值数组 -

  • 如果对象已经将此值作为键,如Netherlands,则创建一个新数组,获取已存在的值(例如:Rotterdam),并添加此值和新值( Amsterdam)到数组,并将此数组设置为Netherlands键的新值。
  • 如果当前值不存在于对象中,请将其设置为新字符串,例如:France是关键字,Paris是值。

代码 -



const city2country = {
    'Amsterdam': 'Netherlands',
    'Rotterdam': 'Netherlands',
    'Paris': 'France',
};

function reverseMapping(obj) {
  let values = Object.values(obj);
  let keys = Object.keys(obj);
  let result = {}
  values.forEach((value, index) => {
    if(!result.hasOwnProperty(value)) {
      // create new entry
      result[value] = keys[index];
    }
    else {
      // duplicate property, create array
      let temp = [];
      // get first value
      temp.push(result[value]);
      // add second value
      temp.push(keys[index]);
      // set value 
      result[value] = temp;
    }
  });
  
  console.log(result);
  return result;
}

reverseMapping(city2country)




这里的好处是 - 它会根据当前对象的结构进行调整 - Netherlands是重复值,在新对象中获取数组作为值,而France得到字符串值Paris作为其属性。当然,改变它应该很容易。

注意 - 旧浏览器可能不支持Object.values()

答案 5 :(得分:0)

@Nina Scholz 的回答非常适合这个确切的问题。 :thumbsup:

但是如果您不需要保留荷兰键 ("Netherlands": ["Amsterdam", "Rotterdam"]) 的两个值,那么这段代码会更短更易于阅读:

const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };

console.log(
  Object.entries(city2country).reduce((obj, item) => (obj[item[1]] = item[0]) && obj, {})
);
// outputs `{Netherlands: "Rotterdam", France: "Paris"}`