I want to reverse the mapping of an object (which might have duplicate values). Example:
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France'
};
reverseMapping(city2country)
Should output:
{
'Netherlands': ['Amsterdam', 'Rotterdam'],
'France': ['Paris']
}
I've come up with the following, naive solution:
const reverseMapping = (obj) => {
const reversed = {};
Object.keys(obj).forEach((key) => {
reversed[obj[key]] = reversed[obj[key]] || [];
reversed[obj[key]].push(key);
});
return reversed;
};
But I'm pretty sure there is a neater, shorter way, preferably prototyped so I could simply do:
const country2cities = city2country.reverse();
You could use Object.assign
, while respecting the given array of the inserted values.
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));
There is no such built-in function in JavaScript. Your code looks fine, but given that there are so many edge cases here that could wrong, I'd suggesting using invertBy
from lodash , which does exactly what you describe.
var object = { 'a': 1, 'b': 2, 'c': 1 }; _.invertBy(object); // => { '1': ['a', 'c'], '2': ['b'] }
You can use something like this to get raid of duplicates first :
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;
});
}
}
and then use this to make it reverse :
function reverseMapping(obj){
var ret = {};
for(var key in obj){
ret[obj[key]] = key;
}
return ret;
}
You could use reduce to save the declaration line reduce .
Abusing &&
to check if the map[object[key]]
is defined first before using Array.concat
.
It's shorter, but is it simpler? Probably not, but a bit of fun ;)
const reverseMapping = (object) =>
Object.keys(object).reduce((map, key) => {
map[object[key]] = map[object[key]] && map[object[key]].concat(key) || [key]
return map;
}, {});
You could try getting an array of values and an array of keys from the current object, and setup a new object to hold the result. Then, as you loop through the array of values -
Netherlands
, you create a new array, fetch the already existing value (ex: Rotterdam
), and add this and the new value ( Amsterdam
) to the array, and set up this array as the new value for the Netherlands
key.France
is the key and Paris
is the value.Code -
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)
The benefit here is - it adjusts to the structure of your current object - Netherlands
being the repeated values, gets an array as it's value in the new object, while France
gets a string value Paris
as it's property. Of course, it should be very easy to change this.
Note - Object.values() might not be supported across older browsers.
@Nina Scholz answer works well for this exact question. :thumbsup:
But if you don't need to keep both values for the Netherlands key ( "Netherlands": ["Amsterdam", "Rotterdam"]
), then this is a little bit shorter and simpler to read:
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"}`
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.