Поменять местами ключ со значением JSON

107

У меня очень большой объект JSON, структурированный следующим образом:

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

Мне нужна функция, которая может обменивать значения ключами в моем объекте, и я не знаю, как это сделать. Мне нужен такой вывод:

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

Есть ли способ, которым я могу это сделать, если бы вручную создал новый объект, в котором все поменялось местами?
Спасибо

C1D
источник
1
все значения являются числами и повторяются ли числа? Если значения повторяются, вы не сможете их поменять местами, поскольку они перезапишут другие, если вы не измените их значение на какое-то уникальное значение. Может быть лучшее решение. В чем причина необходимости подкачки?
Патрик Эванс
@PatrickEvans Это все числа, но они не повторяются. Я пытаюсь сделать простой шифр.
C1D
2
Стоит отметить, что подобная операция «подкачки» проблематична [по крайней мере] по двум причинам: 1) значения преобразуются в строки, когда они становятся ключами, поэтому не удивляйтесь, если у вас появятся неожиданные ключи «[object Object]». И 2) повторяющиеся значения (после преобразования в String) перезаписываются. # 1, по крайней мере, можно решить, создав карту вместо объекта, таким образом:function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}
broofa

Ответы:

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

Пример здесь FIDDLE , не забудьте включить консоль, чтобы увидеть результаты.


Версии ES6 :

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

Или используя Array.reduce () и Object.keys ()

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

Или используя Array.reduce () и Object.entries ()

static objectFlip(obj) {
  return Object.entries(obj).reduce((ret, entry) => {
    const [ key, value ] = entry;
    ret[ value ] = key;
    return ret;
  }, {});
}
jPO
источник
45

вы можете использовать функцию lodash _.invert, она также может использовать 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'] }
Охад Садан
источник
41

Использование ES6:

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
Grappeq
источник
2
Кажется, лучшее решение на сегодняшний день (2019)! Кто-нибудь может подтвердить? Производительность хорошая? Семантика идеальна: мы видим, что это действительно простое решение «сопоставить и поменять местами».
Питер Краусс
1
@ peter-krauss, Вы можете повозиться с jsben.ch/gHEtn на случай, если я что-то пропустил, но обычное сравнение этого ответа и ответа jPO показывает, что цикл for jPO превосходит подход grappeq map почти в 3 к 1 (по крайней мере, в моем браузере).
Майкл Хейс
36

Получите ключи объекта, а затем используйте функцию уменьшения массива, чтобы пройти по каждому ключу и установить значение как ключ, а ключ как значение.

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

В ES6 / ES2015 вы можете комбинировать использование Object.keys и уменьшить с новой функцией Object.assign, функцией стрелки и вычисляемым именем свойства для довольно простого решения с одним оператором.

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

Если вы транспилируете с помощью оператора распространения объекта (этап 3 на момент написания этого текста), это еще больше упростит ситуацию.

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

Наконец, если у вас есть Object.entries (этап 4 на момент написания), вы можете немного очистить логику (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: Неожиданный токен (53:45) 51 | если (btoa) {52 | записи = Object.entries (записи)> 53 | .reduce ((объект, [ключ, значение]) => ({... объект, [значение]: ключ}), {}); | ^
Superlokkus
@Superlokkus Моя лучшая интерпретация этой ошибки заключается в том, что вы не транслируете синтаксис распределения объектов, и на сегодняшний день я не знаю никаких движков JavaScript, которые поддерживают его изначально.
joslarson
1
См. Решение @grappeq, кажется лучше (самое простое!).
Питер Краусс
16

Теперь, когда у нас есть Object.fromEntries:

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

или

obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => ([v, k])))
Sc0ttyD
источник
Должен быть стандартным способом обмена объектами с Node.js версии 12.
Damaged Organic
4

В качестве дополнения к ответам @joslarson и @jPO:
без необходимости ES6 вы можете использовать и оператор запятой :Object.keys Array.reduce

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

Некоторым это может показаться уродливым, но это «отчасти» быстрее, так как reduceне распространяется все свойства цикла objна каждый цикл.

Вайдд4
источник
Тем не менее ... ответ jPO превосходит этот почти в 2: 1. jsben.ch/R75aI (я знаю, что ваш комментарий был давным-давно, но я комментировал ответ grappeq и решил, что я также запустил бы ваш пример).
Майкл Хейс
В целом, цикл for работает быстрее forEach, чем методы mapили reduce. Я сделал этот ответ, чтобы иметь однострочное решение без необходимости использования es6 и при этом оставаться актуальным с точки зрения скорости / эффективности.
Vaidd4
Июнь 2019 года, и это уже не так в последней версии Google Chrome, разница практически отсутствует (9:10)
Иван Кастелланос,
Это кажется наиболее производительным из вариантов ES6: jsben.ch/HvICq
Брайан М. Хант
2

Самый короткий, который я придумал, используя 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
источник
1

Используя Ramda :

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

const result = swapKeysWithValues(source);
им.панкратов
источник
1

Я считаю, что лучше выполнять эту задачу с помощью модуля npm, например invert-kv.

invert-kv : инвертировать ключ / значение объекта. Пример: {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
Почему использование еще одной библиотеки лучше, чем предлагает однострочное решение, такое как @ Sc0ttyD, или даже простой цикл for?
Arel
1

С чистой Ramda в чистом и бессмысленном стиле:

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

Или, с немного более запутанной версией ES6, все еще чисто функциональной:

const swapKeysAndValues2 = obj => Object
    .entries(obj)
    .reduce((newObj, [key, value]) => ({...newObj, [value]: key}), {})
Пьетро Беллоне
источник
1

Вот чисто функциональная реализация переворачивания keysи valuesв ES6:

Машинопись

  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
    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
Откуда это objectвзялось?
Maxime Launois
и это не то, что mapдолжно делать, вы используете это здесь какforEach
barbsan
Я никогда не использовал forEach. Я использую каждую функцию underscore.js, поэтому не знал о forEach. Спасибо @barbsan
Ануп Агарвал
0
function swapKV(obj) {
  const entrySet = Object.entries(obj);
  const reversed = entrySet.map(([k, v])=>[v, k]);
  const result = Object.fromEntries(reversed);
  return result;
}

Это может сделать ваш объект {A : 1, B : 2, C : 3, D : 4}похожим на массив, поэтому вы можете иметь

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

Вот вариант, который заменит ключи значениями, но не потеряет дубликаты, если ваш объект: {a: 1, b: 2, c: 2}, он всегда будет возвращать массив на выходе:

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
источник