Zamień klucz na wartość JSON

106

Mam bardzo duży obiekt JSON o następującej strukturze:

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

Potrzebuję funkcji, która może zamienić wartości z kluczami w moim obiekcie i nie wiem, jak to zrobić. Potrzebowałbym takiego wyniku:

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

Czy jest jakiś sposób, żebym mógł to zrobić, aby ręcznie utworzyć nowy obiekt, w którym wszystko jest zamienione?
Dzięki

C1D
źródło
1
czy wszystkie wartości są liczbami i czy liczby się powtarzają? Jeśli wartości się powtarzają, nie będzie można ich zamienić, ponieważ nadpiszą inne, chyba że zmienisz ich wartość na jakąś unikalną wartość. Może być lepsze rozwiązanie, jaki jest powód potrzeby wymiany?
Patrick Evans,
@PatrickEvans Wszystkie są liczbami, ale się nie powtarzają. Próbuję stworzyć podstawowy szyfr.
C1D
2
Warto zauważyć, że operacja „zamiany”, taka jak ta, jest problematyczna z [co najmniej] dwóch powodów: 1) Wartości są rzutowane na ciągi, gdy stają się kluczami, więc nie zdziw się, gdy masz nieoczekiwane klucze „[obiekt obiektu]”. I 2) zduplikowane wartości (po rzutowaniu na String) zostaną nadpisane. Przynajmniej # 1 można rozwiązać, tworząc mapę zamiast obiektu, a function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}
zatem

Odpowiedzi:

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

Przykład tutaj FIDDLE nie zapomnij włączyć konsoli, aby zobaczyć wyniki.


Wersje ES6 :

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

Lub za pomocą Array.reduce () & Object.keys ()

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

Lub za pomocą Array.reduce () & Object.entries ()

static objectFlip(obj) {
  return Object.entries(obj).reduce((ret, entry) => {
    const [ key, value ] = entry;
    ret[ value ] = key;
    return ret;
  }, {});
}
jPO
źródło
44

możesz użyć funkcji lodash _.invert, ale może również użyć 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
źródło
39

Korzystanie z ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
grappeq
źródło
2
Wydaje się, że jest to lepsze rozwiązanie na dziś (2019)! Ktoś może potwierdzić, prawda? Wydajność dobra? Semantyka jest doskonała: widzimy, że jest to naprawdę proste rozwiązanie typu „mapuj i zamień”.
Peter Krauss,
1
@ peter-krauss, Możesz majstrować przy jsben.ch/gHEtn na wypadek, gdyby coś przeoczyłem, ale przypadkowe porównanie tej odpowiedzi z odpowiedzią jPO pokazuje, że pętla for-jPO przewyższa podejście grappeq do mapy o prawie 3 do 1 (przynajmniej w mojej przeglądarce).
Michael Hays
36

Pobierz klucze obiektu, a następnie użyj funkcji redukcji Array, aby przejść przez każdy klucz i ustawić wartość jako klucz, a klucz jako wartość.

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
źródło
30

W ES6 / ES2015 można łączyć korzystanie z Object.keys i zmniejszyć z nowym Object.assign funkcji, z funkcją strzałki oraz komputerowej nazwy właściwości dla całkiem proste rozwiązanie pojedynczej instrukcji.

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

Jeśli transpilujesz za pomocą operatora rozprzestrzeniania obiektów (etap 3 w momencie pisania tego), to uprości to nieco dalej.

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

Wreszcie, jeśli masz dostępne Object.entries (etap 4 w trakcie pisania), możesz wyczyścić logikę odrobinę bardziej (IMO).

const foo = { a: 1, b: 2, c: 3 };
const bar = Object.entries(foo)
    .reduce((obj, [key, value]) => ({ ...obj, [value]: key }), {});
joslarson
źródło
SyntaxError: /Users/markus/Entwicklung/IT1_Beleg/public/es6/vokabeltrainer.js: Nieoczekiwany token (53:45) 51 | jeśli (btoa) {52 | wpisy = Object.entries (wpisy)> 53 | .reduce ((obj, [klucz, wartość]) => ({... obj, [wartość]: klucz}), {}); | ^
Superlokkus
@Superlokkus Moją najlepszą interpretacją tego wyniku błędu jest to, że nie transpilujesz składni rozprzestrzeniania obiektów, a na dzień dzisiejszy nie znam żadnych silników JavaScript, które obsługują to natywnie.
joslarson
1
Zobacz rozwiązanie @ grappeq, wydaje się lepsze (najprostsze!).
Peter Krauss,
16

Teraz, gdy mamy Object.fromEntries:

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

lub

obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => ([v, k])))
Sc0ttyD
źródło
Powinien być również standardowym sposobem dokonywania zamiany obiektów od wersji 12 Node.js.
Uszkodzony organiczny
4

Jako uzupełnienie odpowiedzi @joslarson i @jPO:
bez ES6 możesz użyć i operatora przecinka :Object.keys Array.reduce

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

Niektórym może się to wydawać brzydkie, ale jest to „trochę” szybsze, ponieważ reducenie rozprzestrzenia wszystkich właściwości objkażdej pętli.

Vaidd4
źródło
Mimo to ... odpowiedź jPO przewyższa tę o prawie 2: 1. jsben.ch/R75aI (Wiem, że twój komentarz był dawno, dawno temu, ale komentowałem odpowiedź grappeqa i pomyślałem, że użyję również twojego przykładu).
Michael Hays,
Całkowicie pętla for pozostaje szybsza niż metody forEach, maplub reduce. Uczyniłem tę odpowiedź, aby mieć jednoliniowe rozwiązanie bez potrzeby es6 i nadal być istotne pod względem szybkości / skuteczności.
Vaidd4
Czy czerwiec 2019 r. I to już nie jest prawdą w najnowszej przeglądarce Google Chrome, różnica jest teraz prawie nieistniejąca (9:10)
Ivan Castellanos
Wydaje się, że jest to najbardziej wydajny z wariantów ES6: jsben.ch/HvICq
Brian M. Hunt.
4

Próbować

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

Kamil Kiełczewski
źródło
2

Najkrótszy, jaki wymyśliłem, używając 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
źródło
1

Korzystanie z Ramdy :

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

const result = swapKeysWithValues(source);
im.pankratov
źródło
1

Uważam, że lepiej wykonać to zadanie, używając modułu npm, np invert-kv.

invert-kv : Odwróć klucz / wartość obiektu. Przykład: {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
źródło
1
Dlaczego użycie kolejnej biblioteki jest lepsze niż rozwiązanie jednowierszowe, jak sugeruje @ Sc0ttyD, lub nawet prosta pętla for?
Arel
1

Z czystą Ramdą w czystym i bezcelowym stylu:

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

Lub, z nieco bardziej zawiłą wersją ES6, nadal czysto funkcjonalną:

const swapKeysAndValues2 = obj => Object
    .entries(obj)
    .reduce((newObj, [key, value]) => ({...newObj, [value]: key}), {})
Pietro Bellone
źródło
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
źródło
3
Skąd to się objectbierze?
Maxime Launois
a to nie jest to, co mappowinno robić, używasz go tutaj jakforEach
barbsan
Nigdy nie używałem forEach. Używam każdej funkcji underscore.js, więc nie wiedziałem o forEach. Dziękuję @barbsan
Anup Agarwal
0

Oto czysta funkcjonalne wdrożenie skakaniu keysi valuesw ES6:

Maszynopis

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

To może sprawić, że twój obiekt będzie {A : 1, B : 2, C : 3, D : 4}podobny do tablicy, więc możesz mieć

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
źródło
0

Oto opcja, która zamieni klucze z wartościami, ale nie straci duplikatów, jeśli twój obiekt to: {a: 1, b: 2, c: 2}, zawsze zwróci tablicę w wyniku:

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
źródło