Intercambiar clave con valor JSON

106

Tengo un objeto JSON extremadamente grande estructurado así:

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

Necesito una función que pueda intercambiar los valores con claves en mi objeto y no sé cómo hacerlo. Necesitaría una salida como esta:

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

¿Hay alguna forma de que pueda hacer esto? ¿Crearía manualmente un nuevo objeto donde todo se intercambia?
Gracias

C1D
fuente
1
¿Son todos los valores números y se repiten los números? Si los valores se repiten, no podrá intercambiarlos, ya que sobrescribirán a los demás, a menos que cambie su valor a un valor único. Podría haber una solución mejor, ¿cuál es la razón por la que se necesita el intercambio?
Patrick Evans
@PatrickEvans Son todos números pero no se repiten. Estoy intentando hacer un cifrado básico.
C1D
2
Vale la pena señalar que una operación de "intercambio" como esta es problemática por [al menos] dos razones: 1) Los valores se envían a cadenas cuando se convierten en claves, así que no se sorprenda cuando tenga claves inesperadas "[objeto Objeto]". Y 2) los valores duplicados (después de convertirlos en String) se sobrescriben. # 1, al menos, puede ser resuelto mediante la producción de un mapa en lugar de un objeto, de esta manera:function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}
broofa

Respuestas:

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

Ejemplo aquí FIDDLE no olvide encender su consola para ver los resultados.


Versiones ES6 :

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

O usando Array.reduce () y Object.keys ()

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

O usando Array.reduce () y Object.entries ()

static objectFlip(obj) {
  return Object.entries(obj).reduce((ret, entry) => {
    const [ key, value ] = entry;
    ret[ value ] = key;
    return ret;
  }, {});
}
jPO
fuente
44

puede usar la función lodash _.invertir también puede usar 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'] }
Ohad Sadan
fuente
39

Usando ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
grappeq
fuente
2
¡Parece la mejor solución para hoy (2019)! Alguien puede confirmarlo, ¿verdad? Buen rendimiento? La semántica es perfecta: podemos ver que es realmente una solución simple de "mapa e intercambio".
Peter Krauss
1
@ peter-krauss, puede jugar con jsben.ch/gHEtn en caso de que me haya perdido algo, pero una comparación casual entre esta respuesta y la respuesta de jPO muestra que el bucle for de jPO supera al enfoque de mapa de grappeq en casi 3 a 1 (al menos en mi navegador).
Michael Hays
36

Obtenga las claves del objeto y luego use la función de reducción de Array para revisar cada clave y establecer el valor como clave y la clave como valor.

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);

Patrick Evans
fuente
30

En ES6 / ES2015 puede combinar el uso de Object.keys y reducir con la nueva función Object.assign , una función de flecha y un nombre de propiedad calculado para una solución de declaración única bastante sencilla.

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

Si está transpilando usando el operador de extensión de objetos (etapa 3 al momento de escribir esto), eso simplificará las cosas un poco más.

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

Finalmente, si tiene Object.entries disponibles (etapa 4 a partir de la escritura), puede limpiar la lógica un poco más (IMO).

const foo = { a: 1, b: 2, c: 3 };
const bar = Object.entries(foo)
    .reduce((obj, [key, value]) => ({ ...obj, [value]: key }), {});
Joslarson
fuente
SyntaxError: /Users/markus/Entwicklung/IT1_Beleg/public/es6/vokabeltrainer.js: Token inesperado (53:45) 51 | si (btoa) {52 | entradas = Objeto.entries (entradas)> 53 | .reduce ((obj, [clave, valor]) => ({... obj, [valor]: clave}), {}); | ^
Superlokkus
@Superlokkus, mi mejor interpretación de ese resultado de error es que no está transpilando la sintaxis de propagación del objeto y, a día de hoy, no conozco ningún motor de JavaScript que lo admita de forma nativa.
Joslarson
1
Vea la solución de @grappeq, parece mejor (¡la más simple!).
Peter Krauss
16

Ahora que tenemos Object.fromEntries:

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

o

obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => ([v, k])))
Sc0ttyD
fuente
También debería ser una forma estándar de hacer intercambio de objetos a partir de la versión 12 de Node.js.
Orgánico dañado
4

Como complemento de las respuestas de @joslarson y @jPO:
Sin necesidad de ES6, puede usar y el operador de coma :Object.keys Array.reduce

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

Algunos pueden encontrarlo feo, pero es "un poco" más rápido ya reduceque no distribuye todas las propiedades de objen cada bucle.

Vaidd4
fuente
Aún así ... la respuesta de jPO supera a esta en casi 2: 1. jsben.ch/R75aI (sé que su comentario fue de hace mucho, mucho tiempo, pero estaba comentando la respuesta de grappeq y pensé que también ejecutaría su ejemplo).
Michael Hays
Totalmente, un bucle for sigue siendo más rápido que los métodos forEach, mapo reduce. Hice esta respuesta para tener una solución de una sola línea sin la necesidad de es6 y seguir siendo relevante en términos de velocidad / efectividad.
Vaidd4
Es junio de 2019 y eso ya no es cierto en la última versión de Google Chrome, la diferencia ahora es casi inexistente (9:10)
Ivan Castellanos
Esta parece la más eficaz
Brian M. Hunt
4

Tratar

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

Kamil Kiełczewski
fuente
2

El más corto que se me ocurrió usando 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
fuente
1

Usando Ramda :

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

const result = swapKeysWithValues(source);
im.pankratov
fuente
1

Creo que es mejor hacer esta tarea usando un módulo npm, como invert-kv.

invert-kv : invierte la clave / valor de un objeto. Ejemplo: {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
fuente
1
¿Por qué usar otra biblioteca es mejor que una solución de una línea como sugiere @ Sc0ttyD, o incluso un bucle for simple?
Arel
1

Con Ramda puro en un estilo puro y sin puntos:

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

O, con una versión ES6 un poco más complicada, todavía puramente funcional:

const swapKeysAndValues2 = obj => Object
    .entries(obj)
    .reduce((newObj, [key, value]) => ({...newObj, [value]: key}), {})
Pietro Bellone
fuente
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);
Anup Agarwal
fuente
3
De donde objectviene esto
Maxime Launois
y esto no es lo que mapse supone que debe hacer, lo estás usando aquí comoforEach
barbsan
Nunca usé forEach. Utilizo las funciones de underscore.js, por lo que no sabía nada sobre forEach. Gracias @barbsan
Anup Agarwal
0

Aquí hay una implementación funcional pura de voltear keysy valuesen ES6:

Mecanografiado

  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
fuente
0
function swapKV(obj) {
  const entrySet = Object.entries(obj);
  const reversed = entrySet.map(([k, v])=>[v, k]);
  const result = Object.fromEntries(reversed);
  return result;
}

Esto puede hacer que su objeto, {A : 1, B : 2, C : 3, D : 4}parezca una matriz, por lo que puede tener

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
fuente
0

Aquí hay una opción que intercambiará claves con valores pero no perderá duplicados, si su objeto es: {a: 1, b: 2, c: 2}, siempre devolverá una matriz en la salida:

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
fuente