Swap key with value in object
Asked Answered
A

25

188

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

Alric answered 11/4, 2014 at 13:11 Comment(4)
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?Phonetist
@PatrickEvans They're all numbers but they don't repeat. I'm trying to make a basic cipher.Alric
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]))}Hymenium
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.Solus
R
186
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;
  }, {});
}
Reparation answered 11/4, 2014 at 13:16 Comment(1)
Object.keys() + Array.prototype.reduce() is the fastest: jsbench.me/y2kzww53oz/1Keilakeily
A
99

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)

Armillda answered 26/6, 2019 at 21:37 Comment(3)
Should be standard way of doing object swap as of Node.js version 12 as well.Acculturate
This is the easiest to understand+most beautiful solution. I think you could drop parents on the return value of the arrow callbackGuelph
While this is indeed the most elegant solution, it's a little slower than using Object.entries() + Array.prototype.reduce(): jsbench.me/y2kzww53oz/1Keilakeily
C
66

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'] }
Chessa answered 8/6, 2016 at 8:41 Comment(3)
multiValue is not supported any moreLepto
Since lodash 4: Instead of the setting multiValue to true you need to use the new method _.invertBy().Mervin
Lodash invert is the fasted implementation by a small margin: jsben.ch/tSjlB It's interesting looking at the implementation: github.com/lodash/lodash/blob/master/invert.jsAposiopesis
M
56

Using ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
Meshach answered 5/10, 2017 at 9:54 Comment(4)
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.Strep
@peter-krauss, You are welcome to tinker with 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).Lethargy
I tried it - 30% slower than jPO's on my computer.Ferine
There a comparison with a few more implementation here: jsben.ch/tSjlBAposiopesis
P
39

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);
Phonetist answered 11/4, 2014 at 13:17 Comment(0)
L
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 }), {});
Lyndialyndon answered 10/5, 2017 at 17:58 Comment(3)
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}), {}); | ^Cornetcy
@Cornetcy 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.Lyndialyndon
See @grappeq's solution, seems better (the simplest!).Strep
R
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.

Rochelrochell answered 11/4, 2021 at 15:49 Comment(1)
This appears to be the one of the faster implementations (at least in chrome 110): jsben.ch/tSjlB The lodash implementation is just a tiny bit faster.Aposiopesis
T
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.

Troglodyte answered 27/12, 2017 at 16:48 Comment(4)
Still... jPO's answer outperforms this one by almost 2:1. 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).Lethargy
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.Troglodyte
Is June 2019 and that is no longer true in latest Google Chrome, the difference is now near non-existent (9:10)Borderline
This appears the most performant of the ES6 variants: jsben.ch/HvICqLister
C
4

Using Ramda:

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

const result = swapKeysWithValues(source);
Cajun answered 28/10, 2017 at 14:18 Comment(1)
Ramda has R.invert and R.invertObj which do the same. The first one puts duplicates in a sublistImpervious
C
3

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>);
Crittenden answered 29/10, 2022 at 7:14 Comment(0)
W
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));
Wrongheaded answered 15/1, 2019 at 22:50 Comment(1)
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!Hultgren
B
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}), {})
Barbados answered 28/5, 2019 at 9:54 Comment(0)
O
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);
Osvaldooswal answered 11/2, 2020 at 16:7 Comment(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:Armillda
L
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);
Lavelle answered 10/6, 2019 at 9:6 Comment(3)
Where does this object come from?Tensor
and this is not what map is supposed to do, you're using it here like forEachMelancon
I never used forEach. I use underscore.js's each function so didn't knew about forEach. Thank you @MelanconLavelle
B
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))
Bengt answered 5/9, 2019 at 17:47 Comment(0)
I
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"]
Interphase answered 15/3, 2020 at 2:38 Comment(0)
E
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"]}
Entry answered 27/3, 2020 at 14:21 Comment(0)
H
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"] }

Harte answered 16/10, 2020 at 11:46 Comment(0)
S
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})), {});
Superhighway answered 20/11, 2020 at 18:15 Comment(0)
E
0
function myFunction(obj) {
  return Object.keys(obj).reduce((acc, cur) => {
    return {  ...acc, [obj[cur]]: cur };
     }, {});
}
Earring answered 3/3, 2021 at 6:47 Comment(0)
W
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), {});
}
Welch answered 6/10, 2021 at 18:53 Comment(0)
K
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'}
Klepht answered 22/2, 2022 at 16:32 Comment(0)
M
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}); 
Mollie answered 19/6, 2022 at 15:25 Comment(0)
N
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;
Nessa answered 1/10, 2022 at 20:45 Comment(0)
G
-2

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'}
Gibraltar answered 6/3, 2019 at 13:46 Comment(2)
Why is using yet another library better than a one-line solution like @Armillda suggests, or even a simple for loop?Trumpetweed
@Trumpetweed for readability. The best practice will depend on your use case. In some use cases, you just want to have the best readable code that is easy to understand for future developers, even for yourself, for maintenance. In other instances, you would prefer fewer dependencies and sophisticated in-line code.Gibraltar

© 2022 - 2025 — McMap. All rights reserved.