Trocar chave com valor JSON

106

Eu tenho um objeto JSON extremamente grande estruturado assim:

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

Preciso de uma função que possa trocar os valores com as chaves do meu objeto e não sei como fazer isso. Eu precisaria de uma saída como esta:

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

Existe alguma maneira de fazer isso criar manualmente um novo objeto onde tudo é trocado?
obrigado

C1D
fonte
1
todos os valores são números e os números se repetem? Se os valores se repetirem, você não poderá trocá-los, pois eles sobrescreverão os outros, a menos que você altere seu valor para algum valor exclusivo. Pode haver uma solução melhor, qual é o motivo para a necessidade da troca?
Patrick Evans
@PatrickEvans São todos números, mas não se repetem. Estou tentando fazer uma cifra básica.
C1D
2
É importante notar que uma operação de "troca" como essa é problemática por [pelo menos] duas razões: 1) Os valores são convertidos para Strings quando se tornam chaves, então não se surpreenda quando você tiver chaves "[Object Object]" inesperadas. E 2) os valores duplicados (depois de convertidos em String) são substituídos. # 1, pelo menos, pode ser resolvido produzindo um Mapa em vez de um Objeto, assim:function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}
broofa 01 de

Respostas:

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

Exemplo aqui FIDDLE não se esqueça de ligar seu console para ver os resultados.


Versões ES6 :

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

Ou usando Array.reduce () & Object.keys ()

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

Ou usando Array.reduce () & Object.entries ()

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

você pode usar a função Lodash _.invert, ela também pode 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
fonte
39

Usando ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
grappeq
fonte
2
Parece a melhor solução para os dias de hoje (2019)! Alguém pode confirmar, não é? Bom desempenho? A semântica é perfeita: podemos ver que é realmente uma solução simples de "mapear e trocar".
Peter Krauss
1
@ peter-krauss, Você é bem-vindo para mexer com jsben.ch/gHEtn caso eu tenha perdido algo, mas uma comparação casual entre esta resposta e a resposta de jPO mostra que o loop for de jPO supera a abordagem de mapa de grappeq em quase 3 para 1 (pelo menos no meu navegador).
Michael Hays de
36

Obtenha as chaves do objeto e use a função de redução do Array para percorrer cada chave e definir o valor como a chave e a chave como o 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
fonte
30

No ES6 / ES2015, você pode combinar o uso de Object.keys e reduzir com a nova função Object.assign , uma função de seta e um nome de propriedade computado para uma solução de instrução única bastante direta.

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

Se você estiver transpilando usando o operador de propagação de objeto (estágio 3 no momento da redação), isso simplificará um pouco mais as coisas.

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

Finalmente, se você tiver Object.entries disponível (estágio 4 no momento da escrita), você pode limpar a lógica um pouco mais (IMO).

const foo = { a: 1, b: 2, c: 3 };
const bar = Object.entries(foo)
    .reduce((obj, [key, value]) => ({ ...obj, [value]: key }), {});
Joslarson
fonte
SyntaxError: /Users/markus/Entwicklung/IT1_Beleg/public/es6/vokabeltrainer.js: Token inesperado (53:45) 51 | if (btoa) {52 | entradas = Object.entries (entradas)> 53 | .reduce ((obj, [chave, valor]) => ({... obj, [valor]: chave}), {}); | ^
Superlokkus
@Superlokkus minha melhor interpretação dessa saída de erro é que você não está transpilando a sintaxe de propagação do objeto e, a partir de hoje, não conheço nenhum mecanismo de JavaScript que a suporte nativamente.
joslarson
1
Veja a solução de @grappeq, parece melhor (a mais simples!).
Peter Krauss
16

Agora que temos Object.fromEntries:

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

ou

obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => ([v, k])))
Sc0ttyD
fonte
Deve ser a maneira padrão de fazer troca de objetos a partir da versão 12 do Node.js.
Danificado orgânico de
4

Como um complemento das respostas @joslarson e @jPO:
Sem a necessidade de ES6, você pode usar e o Operador de vírgula :Object.keys Array.reduce

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

Alguns podem achar feio, mas é "meio" mais rápido, pois o reducenão espalha todas as propriedades de objem cada loop.

Vaidd4
fonte
Ainda assim ... a resposta de jPO supera esta em quase 2: 1. jsben.ch/R75aI (Eu sei que seu comentário foi de muito, muito tempo atrás, mas eu estava comentando sobre a resposta de grappeq e pensei em executar seu exemplo também).
Michael Hays de
Totalmente, um loop for permanece mais rápido que os métodos forEach, mapou reduce. Fiz essa resposta para ter uma solução one-liner sem a necessidade de es6 e ainda ser relevante em termos de velocidade / eficácia.
Vaidd4
É junho de 2019 e isso não é mais verdade no mais recente Google Chrome, a diferença agora é quase inexistente (9:10)
Ivan Castellanos
Esta parece ser a de maior desempenho das variantes ES6: jsben.ch/HvICq
Brian M. Hunt
4

Experimentar

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

Kamil Kiełczewski
fonte
2

O mais curto que criei 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
fonte
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
fonte
1

Acredito que seja melhor fazer essa tarefa usando um módulo npm, como invert-kv.

invert-kv : Inverte a chave / valor de um objeto. Exemplo: {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
fonte
1
Por que usar outra biblioteca é melhor do que uma solução de uma linha como sugere @ Sc0ttyD, ou mesmo um loop for simples?
Arel
1

Com Ramda pura em um estilo puro e sem pontos:

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

Ou, com uma versão ES6 um pouco mais complicada, ainda puramente funcional:

const swapKeysAndValues2 = obj => Object
    .entries(obj)
    .reduce((newObj, [key, value]) => ({...newObj, [value]: key}), {})
Pietro Bellone
fonte
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
fonte
3
De onde vem isso object?
Maxime Launois de
e isso não é o que mapdeveria fazer, você está usando aqui comoforEach
barbsan
Eu nunca usei forEach. Eu uso cada função de underscore.js, então não sabia sobre forEach. Obrigado @barbsan
Anup Agarwal
0

Aqui está uma implementação funcional pura de inversão keyse valuesem 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
fonte
0
function swapKV(obj) {
  const entrySet = Object.entries(obj);
  const reversed = entrySet.map(([k, v])=>[v, k]);
  const result = Object.fromEntries(reversed);
  return result;
}

Isso pode tornar seu objeto, {A : 1, B : 2, C : 3, D : 4}semelhante a um array, para que você possa ter

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

Aqui está uma opção que irá trocar chaves com valores, mas não perder duplicatas, se seu objeto for: {a: 1, b: 2, c: 2}, ele sempre retornará um array na saída:

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
fonte