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

У меня очень большой объект JSON со следующей структурой:

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

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

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

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


person C1D    schedule 11.04.2014    source источник
comment
все значения являются числами и повторяются ли числа? Если значения повторяются, вы не сможете их поменять местами, поскольку они перезапишут другие, если вы не измените их значение на какое-то уникальное значение. Может быть лучшее решение. В чем причина необходимости подкачки?   -  person Patrick Evans    schedule 11.04.2014
comment
@PatrickEvans Это все числа, но они не повторяются. Я пытаюсь сделать простой шифр.   -  person C1D    schedule 11.04.2014
comment
Стоит отметить, что подобная операция подкачки проблематична [по крайней мере] по двум причинам: 1) значения преобразуются в строки, когда они становятся ключами, поэтому не удивляйтесь, если у вас появятся неожиданные ключи [object Object]. И 2) повторяющиеся значения (после преобразования в String) перезаписываются. # 1, по крайней мере, можно решить, создав карту вместо объекта, таким образом: function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}   -  person broofa    schedule 02.02.2019
comment
const swap = o => Object.entries(o).reduce((r, [k, v]) => (r[v]=k, r), {}); Аналогичный подход можно использовать для массивов, значения которых являются положительными целыми числами / индексами.   -  person mpapec    schedule 21.10.2020


Ответы (19)


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;
  }, {});
}
person jPO    schedule 11.04.2014

вы можете использовать функцию lodash _.invert, она также может использовать многоязычный

 var object = { 'a': 1, 'b': 2, 'c': 1 };

  _.invert(object);
  // => { '1': 'c', '2': 'b' }

  // with `multiValue`
  _.invert(object, true);
  // => { '1': ['a', 'c'], '2': ['b'] }
person Ohad Sadan    schedule 08.06.2016
comment
multiValue больше не поддерживается - person Rudy Huynh; 18.06.2021

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

const obj = { a: "aaa", b: "bbb", c: "ccc", d: "ddd" };
Object.assign({}, ...Object.entries(obj).map(([a,b]) => ({ [b]: a })))
person grappeq    schedule 05.10.2017
comment
Кажется лучшим решением на сегодняшний день (2019 г.)! Кто-нибудь может подтвердить? Производительность хорошая? Семантика идеальна: мы видим, что это действительно простое решение для карты и обмена. - person Peter Krauss; 03.01.2019
comment
@ peter-krauss, пожалуйста, поработайте с jsben.ch/gHEtn на случай, если я что-то пропустил, но Случайное сравнение этого ответа и ответа jPO показывает, что цикл for-loop jPO превосходит подход grappeq map почти в 3 раза (по крайней мере, в браузере my). - person Michael Hays; 20.02.2019
comment
Я попробовал - на 30% медленнее, чем у jPO на моем компьютере. - person yeah22; 30.03.2021

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

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

person Patrick Evans    schedule 11.04.2014

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

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

or

obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => [v, k]))

(Обновлено для удаления лишних скобок - спасибо @ devin-g-rhode)

person Sc0ttyD    schedule 26.06.2019
comment
Это должен быть стандартный способ обмена объектами с Node.js версии 12. - person Damaged Organic; 16.07.2019
comment
Это самое простое для понимания + самое красивое решение. Я думаю, вы могли бы отбросить родителей на возвращаемое значение обратного вызова стрелки - person Devin Rhode; 21.09.2020
comment
Это должен быть первый ответ - person eplaut; 14.04.2021

В ES6 / ES2015 вы можете комбинировать использование Object. ключи и уменьшить с новой функцией 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 }), {});
person joslarson    schedule 10.05.2017
comment
SyntaxError: /Users/markus/Entwicklung/IT1_Beleg/public/es6/vokabeltrainer.js: Неожиданный токен (53:45) 51 | если (btoa) {52 | записи = Object.entries (записи) ›53 | .reduce ((объект, [ключ, значение]) = ›({... объект, [значение]: ключ}), {}); | ^ - person Superlokkus; 16.06.2017
comment
@Superlokkus Моя лучшая интерпретация этой ошибки заключается в том, что вы не транслируете синтаксис распределения объектов, и на сегодняшний день я не знаю никаких движков JavaScript, которые поддерживают его изначально. - person joslarson; 26.07.2017
comment
См. Решение @grappeq, кажется лучше (самое простое!). - person Peter Krauss; 03.01.2019

В качестве дополнения к ответам @joslarson и @jPO:
Без ES6 вы можете использовать _ 1_ _ 2_ и Оператор-запятая:

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

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

person Vaidd4    schedule 27.12.2017
comment
Тем не менее ... ответ jPO превосходит этот почти в 2: 1. jsben.ch/R75aI (я знаю, что ваш комментарий был давным-давно, но я комментировал ответ grappeq и подумал, что я бы запустил и ваш пример). - person Michael Hays; 20.02.2019
comment
В целом цикл for остается быстрее, чем методы forEach, map или reduce. Я сделал этот ответ, чтобы иметь однострочное решение без необходимости в es6 и при этом оставаться актуальным с точки зрения скорости / эффективности. - person Vaidd4; 22.02.2019
comment
Это июнь 2019 года, и это уже не так в последней версии Google Chrome, разница почти отсутствует (9:10) - person Ivan Castellanos; 15.06.2019
comment
Это наиболее производительный из вариантов ES6: jsben.ch/HvICq - person Brian M. Hunt; 05.07.2019

Пытаться

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

let obj = {A : 1, B : 2, C : 3, D : 4};

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

console.log(swap(obj));

person Kamil Kiełczewski    schedule 15.01.2019
comment
примечание: r={} создает пустой объект, в котором хранятся новые ключи и значения. Автор мог бы использовать return r, но использовал &&r как способ сделать код коротким, поскольку [undefined]&&{} всегда будет возвращать объект. В целом довольно хитрый трюк! - person Jacksonkr; 22.03.2021

Ответ 2021 года

Краткий способ использования синтаксиса ES6, подобного этому.

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

console.log(
  Object.entries(obj).reduce((acc, [key, value]) => (acc[value] = key, acc), {})
);

person Nguyễn Văn Phong    schedule 11.04.2021

Используя Ramda:

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

const result = swapKeysWithValues(source);
person im.pankratov    schedule 28.10.2017

Я считаю, что лучше выполнять эту задачу с помощью модуля 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'}
person Huan    schedule 06.03.2019
comment
Почему использование еще одной библиотеки лучше, чем предлагает однострочное решение, такое как @ Sc0ttyD, или даже простой цикл for? - person Arel; 29.06.2019

С чистым 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}), {})
person Pietro Bellone    schedule 28.05.2019

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

person Frizzo    schedule 11.02.2020
comment
Если вы выйдете за пределы примерно 50 или около того ключей, вы начнете видеть низкую производительность этого метода. Хотя разворот выглядит и приятно ощущается, вы создаете новый объект и назначаете все ключи из старого объекта каждый раз, когда проходите цикл. Вы получите гораздо лучшую производительность, если назначите функцию редуктора аккумулятору: - person Sc0ttyD; 29.11.2020

Вот чистая функциональная реализация переворачивания 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))

person Artur Grigio    schedule 05.09.2019

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"]
person Garrett    schedule 15.03.2020

Вот вариант, который заменит ключи значениями, но не потеряет дубликаты, если ваш объект: {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"]}
person edi9999    schedule 27.03.2020

Простой вариант TypeScript:

const reverseMap = (map: { [key: string]: string }) => {
    return Object.keys(map).reduce((prev, key) => {
        const value = map[key];
        return { ...prev, [value]: [...(prev.value || []), key] };
    }, {} as { [key: string]: [string] })
}

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

const map = { "a":"1", "b":"2", "c":"2" };
const reversedMap = reverseMap(map);
console.log(reversedMap);

Распечатки: { "1":["a"], "2":["b", "c"] }

person Raphael    schedule 16.10.2020

Перезапись ответа @ Vaidd4, но с использованием Object.assign < / a> (вместо оператора запятой ):

/**
 * Swap object keys and values
 * @param {Object<*>} obj
 * @returns {Object<string>}
 */
function swapObject(obj) {
    return Object.keys(obj).reduce((r, key) => (Object.assign(r, {
        [obj[key]]: key,
    })), {});
}

Или короче:

Object.keys(obj).reduce((r, key) => (Object.assign(r, {[obj[key]]: key})), {});
person thybzi    schedule 20.11.2020

person    schedule
comment
Откуда это object? - person Maxime Launois; 10.06.2019
comment
и это не то, что должен делать map, вы используете его здесь как forEach - person barbsan; 10.06.2019
comment
Я никогда не использовал forEach. Я использую каждую функцию underscore.js, поэтому не знал о forEach. Спасибо @barbsan - person Anup Agarwal; 11.06.2019