Клавіша свопу зі значенням JSON


106

У мене надзвичайно великий об’єкт JSON, структурований так:

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

Мені потрібна функція, яка може міняти значення ключами в моєму об'єкті, і я не знаю, як це зробити. Мені знадобиться такий вихід:

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

Чи є спосіб, що я можу це зробити, створив би вручну новий об’єкт, де все поміняється?
Дякую


1
чи всі цифри значень і чи повторюються числа? Якщо значення повторюються, то ви не зможете поміняти їх, оскільки вони замінять інші, якщо ви не зміните їх значення на якесь унікальне значення. Можливо, буде кращим рішенням, що є причиною необхідності заміни?
Патрік Еванс

@PatrickEvans Це всі числа, але вони не повторюються. Я намагаюся зробити базовий шифр.
C1D

2
Варто зазначити, що така операція "swap", як ця, є проблематичною з [принаймні] двох причин: 1) Значення передаються Strings, коли вони стають ключами, тому не дивуйтеся, коли у вас є несподівані клавіші "[object Object]" ". І 2) повторювані значення (після передачі в String) перезаписуються. Принаймні №1, можна вирішити, function swap(obj) {return new Map(Object.entries(x).map([k, v] => [v, k]))}
створивши

Відповіді:


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

44

ви можете використовувати функцію 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'] }

39

Використання ES6:

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

2
Здається, краще рішення на сьогодні (2019 рік)! Хтось може підтвердити, чи не так? Продуктивність хороша? Семантичний є ідеальним: ми можемо побачити, що це дійсно просте рішення "карта та своп".
Пітер Краус

1
@ peter-krauss, ласкаво просимо познайомитись з jsben.ch/gHEtn, якщо я щось пропустив, але випадкове порівняння між цією відповіддю та відповіддю jPO показує, що jPO's for-loop перевершує підхід до карти grappeq майже на 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 | якщо (бтоа) {52 | entries = Object.entries (entries)> 53 | .reduce ((obj, [ключ, значення]) => ({... obj, [значення]: ключ}), {}); | ^
Суперлоккус

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

Повинен бути стандартний спосіб здійснення обміну об'єктами, як і у версії 12 Node.js.
Пошкоджені органічні

4

В якості доповнення @joslarson і @jPO відповідей:
Без ES6 необхідності можна використовувати і Comma Operator :Object.keys Array.reduce

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

Деякі можуть вважати це некрасивим, але це "якось швидше", оскільки reduceвоно не поширює всі властивості objкожного циклу.


Але відповідь jPO перевершує цю майже 2: 1. jsben.ch/R75aI (я знаю, що ваш коментар був з давніх-давніх часів, але я коментував відповідь grappeq і подумав, що також буду запускати ваш приклад).
Майкл Хейс

Загалом цикл for for залишається швидшим, ніж методи forEach, mapабо reduceметоди. Я зробив цю відповідь, щоб мати однолінійне рішення без необхідності es6 і все ще актуально з точки зору швидкості / ефективності.
Вайд4,

Чи є червень 2019 року, і це вже не вірно в останній версії Google Chrome, різниця зараз майже не існує (9:10)
Іван Кастелланос

Здається, найбільш ефективні варіанти ES6
Brian M. Hunt


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


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, або навіть просте для циклу?
Арель

1

З чистою Рамдою в чистому та безглуздому стилі:

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

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походить?
Максим Ланьоа

і це не те, що mapслід робити, ви використовуєте його тут якforEach
barbsan

Я ніколи не використовував для кожного. Я використовую кожну функцію underscore.js, тому не знав про ForEach. Дякую @barbsan
Agarwal

0

Ось чиста функціональна реалізація гортання keysта valuesв 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))


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"]}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.