สลับคีย์ด้วยค่า JSON


107

ฉันมีออบเจ็กต์ JSON ขนาดใหญ่มากที่มีโครงสร้างดังนี้:

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

ฉันต้องการฟังก์ชั่นที่สามารถสลับค่าด้วยคีย์ในวัตถุของฉันและฉันไม่รู้จะทำอย่างไร ฉันต้องการผลลัพธ์เช่นนี้:

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

มีวิธีใดบ้างที่ฉันสามารถทำได้เพื่อสร้างวัตถุใหม่ด้วยตนเองที่มีการสลับทุกอย่าง
ขอบคุณ


1
เป็นตัวเลขค่าทั้งหมดและตัวเลขจะทำซ้ำหรือไม่ หากค่าเกิดซ้ำคุณจะไม่สามารถสลับได้เนื่องจากจะเขียนทับค่าอื่น ๆ เว้นแต่คุณจะเปลี่ยนค่าเป็นค่าที่ไม่ซ้ำกัน อาจมีทางออกที่ดีกว่าสาเหตุที่ต้องใช้ swap คืออะไร?
Patrick Evans

@PatrickEvans พวกเขาเป็นตัวเลขทั้งหมด แต่ไม่ซ้ำกัน ฉันกำลังพยายามสร้างรหัสพื้นฐาน
C1D

2
เป็นที่น่าสังเกตว่าการดำเนินการ "สลับ" เช่นนี้เป็นปัญหาสำหรับ [อย่างน้อย] สาเหตุสองประการ: 1) ค่าจะถูกโยนไปยังสตริงเมื่อกลายเป็นคีย์ดังนั้นอย่าแปลกใจเมื่อคุณมีคีย์ "[object Object]" ที่ไม่คาดคิด และ 2) ค่าที่ซ้ำกัน (หลังจาก cast to 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;
  }, {});
}


41

ใช้ ES6:

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

2
ดูเหมือนจะเป็นทางออกที่ดีกว่าสำหรับ nowdays (2019)! มีใครยืนยันได้ไหม ประสิทธิภาพดีไหม ความหมายนั้นสมบูรณ์แบบเราจะเห็นได้ว่านั่นเป็นวิธีแก้ปัญหา "map and swap" ที่เรียบง่าย
Peter Krauss

1
@ peter-krauss คุณยินดีต้อนรับคนจรจัดด้วยjsben.ch/gHEtnในกรณีที่ฉันพลาดบางสิ่งไป แต่การเปรียบเทียบแบบสบาย ๆ ระหว่างคำตอบนี้กับคำตอบของ jPO แสดงให้เห็นว่าการ for-loop ของ jPO นั้นมีประสิทธิภาพดีกว่าแนวทางแผนที่ของ grappeq เกือบ 3 ต่อ 1 (อย่างน้อย บนเบราว์เซอร์ของฉัน )
Michael Hays

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 | entries = Object.entries (รายการ)> 53 | .reduce ((obj, [คีย์, ค่า]) => ({... obj, [value]: key}), {}); | ^
Superlokkus

@Superlokkus การตีความที่ดีที่สุดของฉันเกี่ยวกับผลลัพธ์ข้อผิดพลาดนั้นคือคุณไม่ได้ถ่ายทอดไวยากรณ์การแพร่กระจายของวัตถุและ ณ วันนี้ฉันไม่รู้ว่ามีเอ็นจิ้น JavaScript ใด ๆ ที่รองรับโดยกำเนิด
joslarson

1
ดูวิธีแก้ปัญหาของ @ grappeq ดูเหมือนจะดีกว่า (ง่ายที่สุด!)
Peter Krauss


4

ในฐานะที่เป็นส่วนเสริมของคำตอบ @joslarson และ @jPO:
หากไม่จำเป็นต้องใช้ ES6 คุณสามารถใช้และตัวดำเนินการจุลภาค :Object.keys Array.reduce

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

บางคนอาจคิดว่ามันน่าเกลียด แต่มันก็ "ค่อนข้างเร็ว" เพราะreduceมันไม่ได้กระจายคุณสมบัติทั้งหมดของobjแต่ละลูป


ถึงกระนั้น ... คำตอบของ jPO ก็ทำได้ดีกว่าคำตอบนี้เกือบ 2: 1 jsben.ch/R75aI (ฉันรู้ว่าความคิดเห็นของคุณมานานแล้วนานมาแล้ว แต่ฉันแสดงความคิดเห็นเกี่ยวกับคำตอบของ grappeq และคิดว่าฉันจะเรียกใช้ตัวอย่างของคุณด้วย)
Michael Hays

โดยสิ้นเชิงสำหรับห่วงยังคงเร็วขึ้นแล้วforEach, mapหรือreduceวิธีการ ฉันตอบสนองนี้เพื่อให้มีโซลูชันซับเดียวโดยไม่จำเป็นต้องใช้ es6 และยังคงเกี่ยวข้องในแง่ของความเร็ว / ประสิทธิผล
Vaidd4

เป็นเดือนมิถุนายน 2019 และนั่นไม่เป็นความจริงอีกต่อไปใน Google Chrome รุ่นล่าสุดความแตกต่างใกล้จะไม่มีอยู่จริงแล้ว (9:10)
Ivan Castellanos

นี่เป็นตัวแปรที่มีประสิทธิภาพมากที่สุดของ ES6: jsben.ch/HvICq
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

ฉันเชื่อว่าการทำงานนี้โดยใช้โมดูล 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 แนะนำหรือแม้แต่การวนซ้ำแบบง่ายๆ
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:

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
    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
Anup Agarwal

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"]}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.