178

I have an extremely large JSON object structured like this:

{A : 1, B : 2, C : 3, D : 4}

I need a function that can swap the values with keys in my object and I don't know how to do it. I would need an output like this:

{1 : A, 2 : B, 3 : C, 4 : D}

Is there any way that I can do this would manually created a new object where everything is swapped?
Thanks

pilchard
  • 12,414
  • 5
  • 11
  • 23
C1D
  • 3,034
  • 2
  • 21
  • 20
  • 1
    are all values numbers and do the numbers repeat? If the values repeat then you wont be able to swap them as they will overwrite the others, unless you change their value to some unique value. There might be a better solution what is the reason for needing the swap? – Patrick Evans Apr 11 '14 at 13:13
  • @PatrickEvans They're all numbers but they don't repeat. I'm trying to make a basic cipher. – C1D Apr 11 '14 at 13:15
  • 2
    It's worth noting that a "swap" operation like this is problematic for [at least] two reasons: 1) Values are cast to Strings when they become keys so don't be surprised when you have unexpected "[object Object]" keys. And 2) duplicate values (after cast to String) get overwritten. #1, at least, can be solved by producing a Map instead of an Object, thusly: `function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}` – broofa Feb 01 '19 at 21:44
  • `const swap = o => Object.entries(o).reduce((r, [k, v]) => (r[v]=k, r), {});` Similar approach can be used for arrays where values are positive integers/indices. – mpapec Oct 21 '20 at 08:21

25 Answers25

185
function swap(json){
  var ret = {};
  for(var key in json){
    ret[json[key]] = key;
  }
  return ret;
}

Example here FIDDLE don't forget to turn on your console to see the results.


ES6 versions:

static objectFlip(obj) {
  const ret = {};
  Object.keys(obj).forEach(key => {
    ret[obj[key]] = key;
  });
  return ret;
}

Or using Array.reduce() & Object.keys()

static objectFlip(obj) {
  return Object.keys(obj).reduce((ret, key) => {
    ret[obj[key]] = key;
    return ret;
  }, {});
}

Or using Array.reduce() & Object.entries()

static objectFlip(obj) {
  return Object.entries(obj).reduce((ret, entry) => {
    const [ key, value ] = entry;
    ret[ value ] = key;
    return ret;
  }, {});
}
Kostas Minaidis
  • 4,681
  • 3
  • 17
  • 25
jPO
  • 2,502
  • 1
  • 17
  • 26
91

Now that we have Object.fromEntries:

const f = obj => Object.fromEntries(Object.entries(obj).map(a => a.reverse()))

console.log(
  f({A : 'a', B : 'b', C : 'c'})
) // => {a : 'A', b : 'B', c : 'C'}

or:

const f = obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => [v, k]))

console.log(
  f({A : 'a', B : 'b', C : 'c'})
) // => {a : 'A', b : 'B', c : 'C'}

(Updated to remove superfluous parentheses - thanks @devin-g-rhode)

Mir-Ismaili
  • 13,974
  • 8
  • 82
  • 100
Sc0ttyD
  • 1,576
  • 1
  • 14
  • 22
64

you can use lodash function _.invert it also can use multivlaue

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

  _.invert(object);
  // => { '1': 'c', '2': 'b' }

  // with `multiValue`
  _.invert(object, true);
  // => { '1': ['a', 'c'], '2': ['b'] }
Edo
  • 3,311
  • 1
  • 24
  • 25
Ohad Sadan
  • 877
  • 6
  • 15
  • 1
    multiValue is not supported any more – Rudy Huynh Jun 18 '21 at 03:34
  • Since lodash 4: Instead of the setting `multiValue` to `true` you need to use the new method `_.invertBy()`. – white_gecko Nov 26 '21 at 10:52
  • Lodash invert is the fasted implementation by a small margin: http://jsben.ch/tSjlB It's interesting looking at the implementation: https://github.com/lodash/lodash/blob/master/invert.js – Sam Hasler Mar 29 '23 at 09:13
56

Using ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
grappeq
  • 693
  • 5
  • 6
  • 3
    Seems **the better solution** for nowdays (2019)! Somebody can confirm, is it? Performance good? Semantic is perfect: we can see that is really a simple "map and swap" solution. – Peter Krauss Jan 03 '19 at 17:41
  • 5
    @peter-krauss, You are welcome to tinker with http://jsben.ch/gHEtn in case I missed something, but a casual comparison between this answer and jPO's answer shows that jPO's for-loop outperforms grappeq's map approach by almost 3 to 1 (at least on _my_ browser). – Michael Hays Feb 20 '19 at 17:45
  • I tried it - 30% slower than jPO's on my computer. – yeah22 Mar 30 '21 at 02:28
  • There a comparison with a few more implementation here: http://jsben.ch/tSjlB – Sam Hasler Mar 29 '23 at 09:17
38

Get the keys of the object, and then use the Array's reduce function to go through each key and set the value as the key, and the key as the value.

const data = {
  A: 1,
  B: 2,
  C: 3,
  D: 4
}
const newData = Object.keys(data).reduce(function(obj, key) {
  obj[data[key]] = key;
  return obj;
}, {});
console.log(newData);
dota2pro
  • 7,220
  • 7
  • 44
  • 79
Patrick Evans
  • 41,991
  • 6
  • 74
  • 87
31

In ES6/ES2015 you can combine use of Object.keys and reduce with the new Object.assign function, an arrow function, and a computed property name for a pretty straightforward single statement solution.

const foo = { a: 1, b: 2, c: 3 };
const bar = Object.keys(foo)
    .reduce((obj, key) => Object.assign({}, obj, { [foo[key]]: key }), {});

If you're transpiling using the object spread operator (stage 3 as of writing this) that will simplify things a bit further.

const foo = { a: 1, b: 2, c: 3 };
const bar = Object.keys(foo)
    .reduce((obj, key) => ({ ...obj, [foo[key]]: key }), {});

Finally, if you have Object.entries available (stage 4 as of writing), you can clean up the logic a touch more (IMO).

const foo = { a: 1, b: 2, c: 3 };
const bar = Object.entries(foo)
    .reduce((obj, [key, value]) => ({ ...obj, [value]: key }), {});
joslarson
  • 1,686
  • 16
  • 13
  • SyntaxError: /Users/markus/Entwicklung/IT1_Beleg/public/es6/vokabeltrainer.js: Unexpected token (53:45) 51 | if (btoa) { 52 | entries = Object.entries(entries) > 53 | .reduce((obj, [key, value]) => ({...obj, [value]: key}), {}); | ^ – Superlokkus Jun 16 '17 at 19:32
  • @Superlokkus my best interpretation of that error output is that you're not transpiling the object spread syntax, and as of today, I don't know of any JavaScript engines that support it natively. – joslarson Jul 26 '17 at 14:47
  • 1
    See @grappeq's solution, seems better (the simplest!). – Peter Krauss Jan 03 '19 at 17:31
17

2021's answer

The concise way by using ES6 syntax like this.

const obj = {A : 1, B : 2, C : 3, D : 4}

console.log(
  Object.entries(obj).reduce((acc, [key, value]) => (acc[value] = key, acc), {})
);

Explain:

(acc[value] = key, acc)

Using Comma operator (,) syntax.

The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand.

Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
  • 1
    This appears to be the one of the faster implementations (at least in chrome 110): http://jsben.ch/tSjlB The lodash implementation is just a tiny bit faster. – Sam Hasler Mar 29 '23 at 09:15
5

As a complement of @joslarson and @jPO answers:
Without ES6 needed, you can use Object.keys Array.reduce and the Comma Operator:

Object.keys(foo).reduce((obj, key) => (obj[foo[key]] = key, obj), {});

Some may find it ugly, but it's "kinda" quicker as the reduce doesn't spread all the properties of the obj on each loop.

Vaidd4
  • 83
  • 2
  • 8
  • Still... jPO's answer outperforms this one by almost 2:1. http://jsben.ch/R75aI (I know your comment was from long, long ago, but I was commenting on grappeq's answer and figured I'd run your example as well). – Michael Hays Feb 20 '19 at 17:51
  • Totally, a for loop remains faster then the `forEach`, `map` or `reduce` methods. I made this response to have a one-liner solution without the need of es6 and still be relevant in terms of speed/effectiveness. – Vaidd4 Feb 22 '19 at 10:35
  • Is June 2019 and that is no longer true in latest Google Chrome, the difference is now near non-existent (9:10) – Ivan Castellanos Jun 14 '19 at 23:39
  • This appears the most performant of the ES6 variants: http://jsben.ch/HvICq – Brian M. Hunt Jul 05 '19 at 19:22
4

Using Ramda:

const swapKeysWithValues = 
  R.pipe(
    R.keys,
    R.reduce((obj, k) => R.assoc(source[k], k, obj), {})
  );

const result = swapKeysWithValues(source);
im.pankratov
  • 1,768
  • 1
  • 19
  • 19
  • Ramda has `R.invert` and `R.invertObj` which do the same. The first one puts duplicates in a sublist – kidroca Jun 27 '22 at 13:30
2

Try

let swap = (o,r={})=> Object.keys(o).map(k=> r[o[k]]=k) && r;

let obj = {A : 1, B : 2, C : 3, D : 4};

let swap = (o,r={})=> Object.keys(o).map(k=> r[o[k]]=k) && r;

console.log(swap(obj));
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
  • 2
    *note:* `r={}` creates an empty object where the new keys & values are stored. The author could have used `return r` but leveraged `&&r` as a way to keep the code short since `[undefined]&&{}` will always return the object. Overall a pretty clever trick! – Jacksonkr Mar 22 '21 at 20:34
2

Modern JS solution:

const swapKeyValue = (object) =>
  Object.entries(object).reduce((swapped, [key, value]) => (
    { ...swapped, [value]: key }
  ), {});

Typescript:

type ValidKey = number | string;

const swapKeyValue = <K extends ValidKey, V extends ValidKey>(
  object: Record<K, V>
): Record<V, K> =>
  Object.entries(object)
    .reduce((swapped, [key, value]) => (
      { ...swapped, [value as ValidKey]: key }
    ), {} as Record<V, K>);
Gibolt
  • 42,564
  • 15
  • 187
  • 127
1

With pure Ramda in a pure and point-free style:

const swapKeysAndValues = R.pipe(
   R.toPairs,
   R.map(R.reverse),
   R.fromPairs,
);

Or, with a little more convoluted ES6 version, still pure functional:

const swapKeysAndValues2 = obj => Object
    .entries(obj)
    .reduce((newObj, [key, value]) => ({...newObj, [value]: key}), {})
1

Shortest one I came up with using ES6..

const original = {
 first: 1,
 second: 2,
 third: 3,
 fourth: 4,
};


const modified = Object
  .entries(original)
  .reduce((all, [key, value]) => ({ ...all, [value]: key }), {});

console.log('modified result:', modified);
Frizzo
  • 432
  • 5
  • 10
  • 1
    If you are going beyond about 50 or so keys, you will start to see poor performance with this method. Although the spread looks and feels nice, you are creating a new object and assigning all of the keys from the old object every time you go through the loop. You get much better performance by having your reducer function assign to the accumulator: – Sc0ttyD Nov 29 '20 at 10:52
0
    var data = {A : 1, B : 2, C : 3, D : 4}
    var newData = {};
    Object.keys(data).forEach(function(key){newData[data[key]]=key});
    console.log(newData);
0

Here is a pure functional implementation of flipping keys and values in ES6:

TypeScript

  const flipKeyValues = (originalObj: {[key: string]: string}): {[key: string]: string} => {
    if(typeof originalObj === "object" && originalObj !== null ) {
      return Object
      .entries(originalObj)
      .reduce((
        acc: {[key: string]: string}, 
        [key, value]: [string, string],
      ) => {
        acc[value] = key
        return acc;
      }, {})
    } else {
      return {};
    }
  }

JavaScript

const flipKeyValues = (originalObj) => {
    if(typeof originalObj === "object" && originalObj !== null ) {
        return Object
        .entries(originalObj)
        .reduce((acc, [key, value]) => {
          acc[value] = key
          return acc;
        }, {})
    } else {
        return {};
    }
}

const obj = {foo: 'bar'}
console.log("ORIGINAL: ", obj)
console.log("FLIPPED: ", flipKeyValues(obj))
Artur Grigio
  • 5,185
  • 8
  • 45
  • 65
0
function swapKV(obj) {
  const entrySet = Object.entries(obj);
  const reversed = entrySet.map(([k, v])=>[v, k]);
  const result = Object.fromEntries(reversed);
  return result;
}

This can make your object, {A : 1, B : 2, C : 3, D : 4}, array-like, so you can have

const o = {A : 1, B : 2, C : 3, D : 4}
const arrayLike = swapKV(o);
arrayLike.length = 5;
const array = Array.from(arrayLike);
array.shift(); // undefined
array; // ["A", "B", "C", "D"]
Garrett
  • 2,936
  • 1
  • 20
  • 22
0

Here is an option that will swap keys with values but not lose duplicates, if your object is : { a: 1, b: 2, c: 2}, it will always return an array in the output :

function swapMap(map) {
    const invertedMap = {};
    for (const key in map) {
        const value = map[key];
        invertedMap[value] = invertedMap[value] || [];
        invertedMap[value].push(key);
    }
    return invertedMap;
}
swapMap({a: "1", b: "2", c: "2"})
// Returns => {"1": ["a"], "2":["b", "c"]}
edi9999
  • 19,701
  • 13
  • 88
  • 127
0

A simple TypeScript variant:

const reverseMap = (map: { [key: string]: string }) => {
    return Object.keys(map).reduce((prev, key) => {
        const value = map[key];
        return { ...prev, [value]: [...(prev.value || []), key] };
    }, {} as { [key: string]: [string] })
}

Usage:

const map = { "a":"1", "b":"2", "c":"2" };
const reversedMap = reverseMap(map);
console.log(reversedMap);

Prints: { "1":["a"], "2":["b", "c"] }

Raphael
  • 3,846
  • 1
  • 28
  • 28
0

Rewriting answer of @Vaidd4, but using Object.assign (instead of comma operator):

/**
 * Swap object keys and values
 * @param {Object<*>} obj
 * @returns {Object<string>}
 */
function swapObject(obj) {
    return Object.keys(obj).reduce((r, key) => (Object.assign(r, {
        [obj[key]]: key,
    })), {});
}

Or, shorter:

Object.keys(obj).reduce((r, key) => (Object.assign(r, {[obj[key]]: key})), {});
thybzi
  • 1,223
  • 13
  • 15
0
function myFunction(obj) {
  return Object.keys(obj).reduce((acc, cur) => {
    return {  ...acc, [obj[cur]]: cur };
     }, {});
}
Mehdi bayat
  • 199
  • 2
  • 13
0

This is the solution that I'm using:

function objSwap(obj, tl = false) {
    return Object.entries(obj).reduce((a, [k, v]) => (a[v = tl ? v.toLowerCase() : v] = k = tl ? k.toLowerCase() : k, a), {});
}

As a bonus: if you need to swap then check some values I added the possibility to lowercase keys and values. Simply you've to set tl = true, else if you don't need it ...

function objSwap(obj) {
    return Object.entries(obj).reduce((a, [k, v]) => (a[v] = k, a), {});
}
Marco Concas
  • 1,665
  • 20
  • 25
0

Using a for...of loop:

let obj = {A : 1, B : 2, C : 3, D : 4}

for (let [key, value] of Object.entries(obj)){
    
    obj[value] = key

    delete obj[key]

}

console.log(obj) // {1: 'A', 2: 'B', 3: 'C', 4: 'D'}
Erik Martín Jordán
  • 4,332
  • 3
  • 26
  • 36
0

ONE OF THE ES6 WAYS IS HERE

const invertObject = (object) =>Object.entries(object).reduce((result, value) =>  ({...result, [value[1]]: value[0] }), {});

let obj = invertObject({A : 1, B : 2, C : 3, D : 4}); 
Jinu Joseph Daniel
  • 5,864
  • 15
  • 60
  • 90
0

Here's a type-safe way using TypeScript that has not been suggested before. This solution takes two generics that means the return type will be typed as expected. It's faster than doing methods with .reduce or Object.entries.

// Easier way to type `symbol | number | string` (the only valid keys of an object)
export type AnyKey = keyof any;

export function mirror<K extends AnyKey, V extends AnyKey>(
    object: Record<K, V>,
) {
    const ret: Partial<Record<V, K>> = {};

    for (const key in object) {
        ret[object[key]] = key;
    }

    return ret as Record<V, K>;
}

Usage:

const obj = mirror({
  a: 'b',
  c: 'd',
});

// {b: 'a', d: 'c'}
obj;
alistair
  • 565
  • 5
  • 15
-1

I believe it's better to do this task by using an npm module, like invert-kv.

invert-kv: Invert the key/value of an object. Example: {foo: 'bar'} → {bar: 'foo'}

https://www.npmjs.com/package/invert-kv

const invertKv = require('invert-kv');

invertKv({foo: 'bar', unicorn: 'rainbow'});
//=> {bar: 'foo', rainbow: 'unicorn'}
Huan
  • 2,876
  • 3
  • 30
  • 49
  • 5
    Why is using yet another library better than a one-line solution like @Sc0ttyD suggests, or even a simple for loop? – Arel Jun 29 '19 at 13:47