วิธีการจัดกลุ่มอาร์เรย์ของวัตถุตามคีย์


154

ไม่มีใครรู้วิธี (lodash ถ้าเป็นไปได้ด้วย) วิธีการจัดกลุ่มวัตถุโดยคีย์วัตถุแล้วสร้างอาร์เรย์ใหม่ของวัตถุตามการจัดกลุ่ม? ตัวอย่างเช่นฉันมีวัตถุรถยนต์มากมาย:

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

ฉันต้องการสร้างวัตถุรถใหม่ที่จัดกลุ่มตามmake:

var cars = {
    'audi': [
        {
            'model': 'r8',
            'year': '2012'
        }, {
            'model': 'rs5',
            'year': '2013'
        },
    ],

    'ford': [
        {
            'model': 'mustang',
            'year': '2012'
        }, {
            'model': 'fusion',
            'year': '2015'
        }
    ],

    'kia': [
        {
            'model': 'optima',
            'year': '2012'
        }
    ]
}

1
คุณมองไปที่groupBy?
SLaks

2
ผลลัพธ์ของคุณไม่ถูกต้อง
Nina Scholz

มีวิธีการคล้ายกันในการรับแผนที่แทนวัตถุหรือไม่?
Andrea Bergonzo

คำตอบ:


104

คำตอบของติโมคือวิธีที่ฉันจะทำ ง่าย_.groupByและอนุญาตให้ทำซ้ำบางอย่างในวัตถุในโครงสร้างที่จัดกลุ่ม

อย่างไรก็ตาม OP ยังขอmakeให้ลบคีย์ที่ซ้ำกัน หากคุณต้องการไปตลอดทาง:

var grouped = _.mapValues(_.groupBy(cars, 'make'),
                          clist => clist.map(car => _.omit(car, 'make')));

console.log(grouped);

อัตราผลตอบแทน:

{ audi:
   [ { model: 'r8', year: '2012' },
     { model: 'rs5', year: '2013' } ],
  ford:
   [ { model: 'mustang', year: '2012' },
     { model: 'fusion', year: '2015' } ],
  kia: [ { model: 'optima', year: '2012' } ] }

หากคุณต้องการที่จะทำเช่นนี้โดยใช้ Underscore.js ทราบว่ารุ่นของการที่เรียกว่า_.mapValues_.mapObject


278

ใน Javascript ธรรมดาคุณสามารถใช้Array#reduceกับวัตถุ

var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
    result = cars.reduce(function (r, a) {
        r[a.make] = r[a.make] || [];
        r[a.make].push(a);
        return r;
    }, Object.create(null));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


1
ฉันจะย้ำresultผลลัพธ์ได้อย่างไร
Mounir Elfassi

1
คุณสามารถทำรายการด้วยObject.entriesและวนซ้ำคู่คีย์ / ค่า
Nina Scholz

มีวิธีการลบออกmakeจากชุดข้อมูลเมื่อจัดกลุ่มหรือไม่ มันใช้พื้นที่มากขึ้น
Mercurial


r และจุดยืนคืออะไร มันจะถูกต้องหรือไม่ที่จะสมมติว่า r คือตัวสะสมและค่าปัจจุบัน?
โอมาร์

68

_.groupBy()คุณกำลังมองหา

การลบคุณสมบัติที่คุณกำลังจัดกลุ่มโดยออกจากวัตถุนั้นจะไม่สำคัญหากจำเป็น:

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},];

var grouped = _.groupBy(cars, function(car) {
  return car.make;
});

console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>


เป็นโบนัสคุณจะได้รับไวยากรณ์ที่ดีกว่าด้วยฟังก์ชั่นลูกศร ES6:

const grouped = _.groupBy(cars, car => car.make);

18
และถ้าคุณต้องการให้มันสั้นลงvar grouped = _.groupBy(cars, 'make');ไม่จำเป็นต้องมีฟังก์ชั่นเลยถ้า accessor เป็นชื่อคุณสมบัติแบบง่าย
Jonathan Eunice

1
'_' หมายถึงอะไร
Adrian Grzywaczewski

@AdrianGrzywaczewski มันเป็นแบบแผนเริ่มต้นสำหรับการเว้นวรรคชื่อ 'lodash' หรือ 'ขีดล่าง' ตอนนี้ librairies เป็นแบบแยกส่วนไม่จำเป็นต้องใช้อีกต่อไป npmjs.com/package/lodash.groupby
vilsbole

5
และฉันจะ interate ในผลลัพธ์ได้อย่างไร
Luis Antonio Pestana

36

เวอร์ชันย่อเพื่อจัดกลุ่มอาร์เรย์ของวัตถุโดยคีย์ที่แน่นอนใน es6:

result = array.reduce((h, obj) => Object.assign(h, { [obj.key]:( h[obj.key] || [] ).concat(obj) }), {})

รุ่นที่ยาวกว่า:

result = array.reduce(function(h, obj) {
  h[obj.key] = (h[obj.key] || []).concat(obj);
  return h; 
}, {})

ดูเหมือนว่าคำถามเดิมถามว่าจะจัดกลุ่มรถยนต์ด้วยการทำได้อย่างไร แต่ไม่ละยี่ห้อในแต่ละกลุ่ม ดังนั้นคำตอบจะเป็นดังนี้:

result = cars.reduce((h, {model,year,make}) => {
  return Object.assign(h, { [make]:( h[make] || [] ).concat({model,year})})
}, {})

นี่ไม่ใช่ es5 แน่นอน
Shinigami

มันใช้งานได้จริง! ผู้ใดสามารถเพิ่มฟังก์ชั่นการลดนี้ได้?
Jeevan

ฉันชอบคำตอบของคุณทั้งคู่ แต่ฉันเห็นว่าทั้งคู่ให้ฟิลด์ "ทำ" ในฐานะสมาชิกของอาร์เรย์ "ทำ" แต่ละรายการ ฉันได้ให้คำตอบตามที่คุณส่งออกตรงกับผลลัพธ์ที่คาดหวัง ขอบคุณ!
Daniel Vukasovich

15

นี่คือgroupByฟังก์ชั่นของคุณเองซึ่งเป็นลักษณะทั่วไปของรหัสจาก: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore

function groupBy(xs, f) {
  return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r), {});
}

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const result = groupBy(cars, (c) => c.make);
console.log(result);


15

var cars = [{
  make: 'audi',
  model: 'r8',
  year: '2012'
}, {
  make: 'audi',
  model: 'rs5',
  year: '2013'
}, {
  make: 'ford',
  model: 'mustang',
  year: '2012'
}, {
  make: 'ford',
  model: 'fusion',
  year: '2015'
}, {
  make: 'kia',
  model: 'optima',
  year: '2012'
}].reduce((r, car) => {

  const {
    model,
    year,
    make
  } = car;

  r[make] = [...r[make] || [], {
    model,
    year
  }];

  return r;
}, {});

console.log(cars);


8

ผมปล่อยREAL GROUP BYตัวอย่าง JS อาร์เรย์เหมือนกันงานนี้ที่นี่

const inputArray = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];

var outObject = inputArray.reduce(function(a, e) {
  // GROUP BY estimated key (estKey), well, may be a just plain key
  // a -- Accumulator result object
  // e -- sequentally checked Element, the Element that is tested just at this itaration

  // new grouping name may be calculated, but must be based on real value of real field
  let estKey = (e['Phase']); 

  (a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e);
  return a;
}, {});

console.log(outObject);


7

คุณสามารถลองปรับเปลี่ยนวัตถุภายในฟังก์ชั่นที่เรียกว่าต่อการทำซ้ำโดย _.groupBy func ขอให้สังเกตว่าแหล่งที่มาเปลี่ยนแปลงองค์ประกอบของเขา!

var res = _.groupBy(cars,(car)=>{
    const makeValue=car.make;
    delete car.make;
    return makeValue;
})
console.log(res);
console.log(cars);

1
แม้ว่ารหัสนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมและวิธีแก้ปัญหานี้จะช่วยปรับปรุงคุณภาพการโพสต์ของคุณได้อย่างไร โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่เพียงแค่คนที่ถามตอนนี้! โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
Makyen

ดูเหมือนว่าคำตอบที่ดีที่สุดสำหรับฉันเนื่องจากคุณผ่านอาร์เรย์เพียงครั้งเดียวเพื่อให้ได้ผลลัพธ์ที่ต้องการ ไม่จำเป็นต้องใช้ฟังก์ชั่นอื่นเพื่อลบmakeคุณสมบัติและอ่านได้ง่ายขึ้นเช่นกัน
Carrm

7

มันเป็นไปได้ด้วยการforวนซ้ำง่ายๆ:

 const result = {};

 for(const {make, model, year} of cars) {
   if(!result[make]) result[make] = [];
   result[make].push({ model, year });
 }

และอาจเร็วกว่าเช่นกันและเรียบง่ายขึ้น ฉันได้ขยายตัวอย่างของคุณให้มีชีวิตชีวาขึ้นเล็กน้อยเนื่องจากฉันมีรายการเขตข้อมูลจำนวนมากจากตาราง db ที่ฉันไม่ต้องการพิมพ์นอกจากนี้โปรดทราบว่าคุณจะต้องแทนที่ const ด้วย let for ( let { TABLE_NAME, ...fields } of source) { result[TABLE_NAME] = result[TABLE_NAME] || []; result[TABLE_NAME].push({ ...fields }); }
adrien

ขอบคุณมาก! medium.com/@mautayro/ ......
adrien

5

สำหรับกรณีที่คีย์อาจเป็นโมฆะและเราต้องการจัดกลุ่มเป็นอื่น ๆ

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},
            {'make':'kia','model':'optima','year':'2033'},
            {'make':null,'model':'zen','year':'2012'},
            {'make':null,'model':'blue','year':'2017'},

           ];


 result = cars.reduce(function (r, a) {
        key = a.make || 'others';
        r[key] = r[key] || [];
        r[key].push(a);
        return r;
    }, Object.create(null));

4

สร้างวิธีการที่สามารถใช้ซ้ำได้

Array.prototype.groupBy = function(prop) {
      return this.reduce(function(groups, item) {
        const val = item[prop]
        groups[val] = groups[val] || []
        groups[val].push(item)
        return groups
      }, {})
    };

จากนั้นด้านล่างคุณสามารถจัดกลุ่มตามเกณฑ์ใด ๆ

const groupByMake = cars.groupBy('make');
        console.log(groupByMake);

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];
  //re-usable method
Array.prototype.groupBy = function(prop) {
	  return this.reduce(function(groups, item) {
		const val = item[prop]
		groups[val] = groups[val] || []
		groups[val].push(item)
		return groups
	  }, {})
	};
  
 // initiate your groupBy. Notice the recordset Cars and the field Make....
  const groupByMake = cars.groupBy('make');
		console.log(groupByMake);
    
    //At this point we have objects. You can use Object.keys to return an array


3

รุ่นต้นแบบที่ใช้ ES6 เช่นกัน โดยพื้นฐานแล้วจะใช้ฟังก์ชั่นการลดเพื่อส่งผ่านรายการสะสมและรายการปัจจุบันซึ่งจะใช้ฟังก์ชันนี้เพื่อสร้างอาร์เรย์ "ที่จัดกลุ่ม" ของคุณโดยยึดตามรหัสผ่านที่ผ่าน ส่วนด้านในของการลดอาจดูซับซ้อน แต่ที่สำคัญคือการทดสอบเพื่อดูว่ากุญแจของวัตถุที่ส่งผ่านนั้นมีอยู่หรือไม่และถ้ามันไม่สร้างอาร์เรย์ว่างเปล่าและผนวกรายการปัจจุบันกับอาร์เรย์ที่สร้างขึ้นใหม่โดยใช้การแพร่กระจาย ผู้ประกอบการส่งผ่านในวัตถุทั้งหมดของอาร์เรย์คีย์ปัจจุบันและผนวกรายการปัจจุบัน หวังว่านี้จะช่วยให้ใครบางคน!.

Array.prototype.groupBy = function(k) {
  return this.reduce((acc, item) => ((acc[item[k]] = [...(acc[item[k]] || []), item]), acc),{});
};

const projs = [
  {
    project: "A",
    timeTake: 2,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 4,
    desc: "this is a description"
  },
  {
    project: "A",
    timeTake: 12,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 45,
    desc: "this is a description"
  }
];

console.log(projs.groupBy("project"));

1

คุณสามารถใช้array#forEach()วิธีการเช่นนี้:

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

let newcars = {}

cars.forEach(car => {
  newcars[car.make] ? // check if that array exists or not in newcars object
    newcars[car.make].push({model: car.model, year: car.year})  // just push
   : (newcars[car.make] = [], newcars[car.make].push({model: car.model, year: car.year})) // create a new array and push
})

console.log(newcars);


1
function groupBy(data, property) {
  return data.reduce((acc, obj) => {
    const key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}
groupBy(people, 'age');

1

แค่ลองอันนี้มันใช้งานได้ดีสำหรับฉัน

let grouped = _.groupBy(cars, 'make');


2
Uncaught ReferenceError: _ ไม่ได้กำหนดไว้ - คุณควรชัดเจนว่าโซลูชันของคุณต้องการติดตั้งไลบรารี่ของบุคคลที่สามเพียงเพื่อแก้ปัญหานี้
metakungfu

1
ขอโทษฉันคิดว่าทุกคนรู้ _ ย่อมาจากและส่วนใหญ่จะใช้สำหรับ lodash lib ดังนั้นคุณต้องใช้ lodash โปรดอ่านคำถามเพื่อให้คุณทราบว่าเขา / เธอกำลังขอบ้านพัก ดีขอบคุณ ฉันจะจำสิ่งนี้ และอย่าลืมเขียน lib
agravat.in

1

ฉันสร้างมาตรฐานเพื่อทดสอบประสิทธิภาพของแต่ละโซลูชันที่ไม่ใช้ไลบรารีภายนอก

JSBen.ch

reduce()ตัวเลือกโพสต์โดย @Nina Scholz น่าจะเป็นที่ดีที่สุดคนหนึ่ง


0

ฉันชอบคำตอบ @metakunfu แต่ไม่ได้ให้ผลลัพธ์ที่คาดหวังอย่างแน่นอน นี่คือการอัปเดตที่กำจัด "ทำ" ในส่วนของ JSON สุดท้าย

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {})

console.log(JSON.stringify(result));

เอาท์พุท:

{  
   "audi":[  
      {  
         "model":"r8",
         "year":"2012"
      },
      {  
         "model":"rs5",
         "year":"2013"
      }
   ],
   "ford":[  
      {  
         "model":"mustang",
         "year":"2012"
      },
      {  
         "model":"fusion",
         "year":"2015"
      }
   ],
   "kia":[  
      {  
         "model":"optima",
         "year":"2012"
      }
   ]
}

0

ด้วย lodash / fp คุณสามารถสร้างฟังก์ชั่นกับ_.flow()กลุ่มที่ 1 โดยใช้คีย์จากนั้นจับคู่แต่ละกลุ่มและละเว้นคีย์จากแต่ละรายการ:

const { flow, groupBy, mapValues, map, omit } = _;

const groupAndOmitBy = key => flow(
  groupBy(key),
  mapValues(map(omit(key)))
);

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const groupAndOmitMake = groupAndOmitBy('make');

const result = groupAndOmitMake(cars);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>


0

สร้างคำตอบโดย @Jonas_Wilms หากคุณไม่ต้องการพิมพ์ในทุกฟิลด์

    var result = {};

    for ( let { first_field, ...fields } of your_data ) 
    { 
       result[first_field] = result[first_field] || [];
       result[first_field].push({ ...fields }); 
    }

ฉันไม่ได้สร้างมาตรฐาน แต่ฉันเชื่อว่าการใช้ลูป for จะมีประสิทธิภาพมากกว่าสิ่งที่แนะนำในคำตอบนี้


0
const reGroup = (list, key) => {
    const newGroup = {};
    list.forEach(item => {
        const newItem = Object.assign({}, item);
        delete newItem[key];
        newGroup[item[key]] = newGroup[item[key]] || [];
        newGroup[item[key]].push(newItem);
    });
    return newGroup;
};
const animals = [
  {
    type: 'dog',
    breed: 'puddle'
  },
  {
    type: 'dog',
    breed: 'labradoodle'
  },
  {
    type: 'cat',
    breed: 'siamese'
  },
  {
    type: 'dog',
    breed: 'french bulldog'
  },
  {
    type: 'cat',
    breed: 'mud'
  }
];
console.log(reGroup(animals, 'type'));
const cars = [
  {
      'make': 'audi',
      'model': 'r8',
      'year': '2012'
  }, {
      'make': 'audi',
      'model': 'rs5',
      'year': '2013'
  }, {
      'make': 'ford',
      'model': 'mustang',
      'year': '2012'
  }, {
      'make': 'ford',
      'model': 'fusion',
      'year': '2015'
  }, {
      'make': 'kia',
      'model': 'optima',
      'year': '2012'
  },
];

console.log(reGroup(cars, 'make'));

0

จัดกลุ่มวัตถุใน typescript ด้วยสิ่งนี้:

groupBy (list: any[], key: string): Map<string, Array<any>> {
    let map = new Map();
    list.map(val=> {
        if(!map.has(val[key])){
            map.set(val[key],list.filter(data => data[key] == val[key]));
        }
    });
    return map;
});

สิ่งนี้ดูไม่มีประสิทธิภาพเมื่อคุณค้นหาแต่ละคีย์ การค้นหามีความซับซ้อนมากที่สุดของ O (n)
Leukipp

0

ฉันชอบที่จะเขียนมันโดยไม่ต้องพึ่งพา / ความซับซ้อนเพียงแค่ js เรียบง่ายบริสุทธิ์

const mp = {}
const cars = [
  {
    model: 'Imaginary space craft SpaceX model',
    year: '2025'
  },
  {
    make: 'audi',
    model: 'r8',
    year: '2012'
  },
  {
    make: 'audi',
    model: 'rs5',
    year: '2013'
  },
  {
    make: 'ford',
    model: 'mustang',
    year: '2012'
  },
  {
    make: 'ford',
    model: 'fusion',
    year: '2015'
  },
  {
    make: 'kia',
    model: 'optima',
    year: '2012'
  }
]

cars.forEach(c => {
  if (!c.make) return // exit (maybe add them to a "no_make" category)

  if (!mp[c.make]) mp[c.make] = [{ model: c.model, year: c.year }]
  else mp[c.make].push({ model: c.model, year: c.year })
})

console.log(mp)


-1

นี่เป็นวิธีแก้ปัญหาอื่น ตามที่ขอ.

ฉันต้องการสร้างวัตถุรถใหม่ที่จัดกลุ่มตามยี่ห้อ:

function groupBy() {
  const key = 'make';
  return cars.reduce((acc, x) => ({
    ...acc,
    [x[key]]: (!acc[x[key]]) ? [{
      model: x.model,
      year: x.year
    }] : [...acc[x[key]], {
      model: x.model,
      year: x.year
    }]
  }), {})
}

เอาท์พุท:

console.log('Grouped by make key:',groupBy())

-1

นี่คือทางออกที่ได้รับแรงบันดาลใจจาก Collector.groupingBy () ใน Java:

function groupingBy(list, keyMapper) {
  return list.reduce((accummalatorMap, currentValue) => {
    const key = keyMapper(currentValue);
    if(!accummalatorMap.has(key)) {
      accummalatorMap.set(key, [currentValue]);
    } else {
      accummalatorMap.set(key, accummalatorMap.get(key).push(currentValue));
    }
    return accummalatorMap;
  }, new Map());
}

สิ่งนี้จะทำให้วัตถุแผนที่

// Usage

const carMakers = groupingBy(cars, car => car.make);

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