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


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;
  }, {});
}

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'] }

39

Korzystanie z ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))

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


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 }), {});

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

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.


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.


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


1

Korzystanie z Ramdy :

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

const result = swapKeysWithValues(source);

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'}

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}), {})

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

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


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"]

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"]}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.