วิธีการรับค่าที่แตกต่างจากอาร์เรย์ของวัตถุใน JavaScript?


383

สมมติว่าฉันมีต่อไปนี้:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

เป็นวิธีที่ดีที่สุดที่จะได้รับอาร์เรย์ของทุกวัยที่แตกต่างกันเช่นที่ฉันได้รับผลของ:

[17, 35]

มีวิธีใดบ้างที่ฉันสามารถจัดโครงสร้างข้อมูลหรือวิธีที่ดีกว่าเพื่อที่ฉันจะได้ไม่ต้องวนซ้ำผ่านแต่ละอาร์เรย์เพื่อตรวจสอบค่าของ "อายุ" และตรวจสอบกับอาร์เรย์อื่นสำหรับการมีอยู่ของมันและเพิ่มถ้าไม่?

หากมีวิธีใดที่ฉันจะสามารถดึงอายุที่แตกต่างออกไปโดยไม่ทำซ้ำ ...

วิธีที่ไม่มีประสิทธิภาพในปัจจุบันฉันต้องการที่จะปรับปรุง ... ถ้ามันหมายความว่าแทนที่จะเป็น "อาร์เรย์" เป็นอาร์เรย์ของวัตถุ แต่เป็น "แผนที่" ของวัตถุที่มีคีย์เฉพาะบางอย่าง (เช่น "1,2,3") ที่จะเป็น ก็โอเคเช่นกัน ฉันแค่มองหาวิธีที่มีประสิทธิภาพสูงสุด

ต่อไปนี้เป็นวิธีที่ฉันทำในขณะนี้ แต่สำหรับฉันการทำซ้ำดูเหมือนจะเป็นเรื่องที่ยากสำหรับประสิทธิภาพแม้ว่ามันจะทำงานได้ ...

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)

34
การทำซ้ำไม่ใช่ "crummy for efficiency" และคุณไม่สามารถทำอะไรกับทุก ๆ องค์ประกอบ "โดยไม่ต้องวนซ้ำ" คุณสามารถใช้วิธีการทำงานที่หลากหลาย แต่ท้ายที่สุดแล้วบางสิ่งในบางระดับต้องทำซ้ำสิ่งต่างๆ
Eevee

// รหัสเรียกใช้ 100% รายการ listOfTags = [{id: 1, ป้ายกำกับ: "Hello", สี: "แดง", การเรียงลำดับ: 0}, {id: 2, ป้ายกำกับ: "โลก", สี: "เขียว", การเรียงลำดับ : 1}, {id: 3, ป้ายกำกับ: "Hello", สี: "blue", การเรียงลำดับ: 4}, {id: 4, ป้ายกำกับ: "Sunshine", สี: "yellow", การเรียงลำดับ: 5}, {id : 5, ป้ายกำกับ: "Hello", สี: "red", การเรียงลำดับ: 6}], keys = ['label', 'color'], filtered = listOfTags.filter ((s => o => (k => ! s.has (k) && s.add (k)) (keys.map (k => o [k]). เข้าร่วม ('|'))) (ชุดใหม่)); console.log (กรอง);
Sandeep Mishra

1
รางวัลที่ดีมาก แต่คำถามที่มีข้อมูลที่กำหนดและคำตอบเป็นคำตอบอยู่ที่นี่แล้ว: stackoverflow.com/questions/53542882/... จุดประสงค์ของเงินรางวัลคืออะไร? ฉันควรตอบปัญหานี้โดยเฉพาะด้วยปุ่มสองปุ่มขึ้นไปหรือไม่
Nina Scholz

Setวัตถุและmaps สิ้นเปลือง งานนี้ใช้.reduce()ขั้นตอนง่าย ๆ
Redu

คำตอบ:


126

ถ้านี่คือ PHP ฉันจะสร้างอาร์เรย์ด้วยปุ่มและใช้เวลาarray_keysสิ้นสุด แต่ JS ไม่มีความหรูหราเช่นนั้น ลองทำสิ่งนี้แทน:

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}

15
ไม่เพราะarray_uniqueจะเปรียบเทียบสินค้าทั้งหมดไม่ใช่แค่อายุตามที่ถามที่นี่
Niet the Dark Absolute

6
ฉันคิดว่าflags = {}ดีกว่าflags = []
zhuguowei

3
@zhuguowei บางทีแม้ว่าจะไม่มีอะไรผิดปกติกับอาร์เรย์หร็อมแหร็ม - และฉันยังสันนิษฐานว่าageเป็นจำนวนเต็มค่อนข้างน้อย (<120 แน่นอน)
Niet the Dark Absolut

เราจะพิมพ์จำนวนผู้คนที่มีอายุเท่ากันได้อย่างไร?
user1788736

605

หากคุณใช้ ES6 / ES2015 หรือใหม่กว่าคุณสามารถทำได้ด้วยวิธีนี้:

const unique = [...new Set(array.map(item => item.age))];

นี่คือตัวอย่างเกี่ยวกับวิธีการทำ


11
ฉันได้รับข้อผิดพลาด:TypeError: (intermediate value).slice is not a function
AngJobs เมื่อ Github

7
@Thomas ... หมายถึงผู้ประกอบการแพร่กระจาย ในกรณีนี้มันหมายถึงการสร้างชุดใหม่และกระจายในรายการ คุณสามารถหาข้อมูลเพิ่มเติมได้ที่นี่: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Vlad Bezden

10
สำหรับ typescript คุณต้องใช้ Set ใหม่ในชุด Array.from () ... ฉันจะให้คำตอบด้านล่าง @AngJobs
Christian Matthew

4
เป็นไปได้หรือไม่ที่จะค้นหาวัตถุที่เป็นเอกลักษณ์โดยยึดตามหลาย ๆ ปุ่มในวัตถุ?
Jefree Sujit

13
วิธีการแก้ปัญหานี้ให้เกือบทุกคำต่อคำไม่กี่เดือนก่อนหน้านี้โดย @Russell Vea คุณตรวจสอบคำตอบที่มีอยู่หรือไม่? แต่แล้วอีกครั้งโหวต 266+ แสดงให้เห็นว่าไม่มีใครตรวจสอบอย่างใดอย่างหนึ่ง
Dan Dascalescu

164

ใช้ ES6

let array = [
  { "name": "Joe", "age": 17 },
  { "name": "Bob", "age": 17 },
  { "name": "Carl", "age": 35 }
];
array.map(item => item.age)
  .filter((value, index, self) => self.indexOf(value) === index)

> [17, 35]

2
ถ้าฉันต้องการได้ค่าด้วยดัชนีฉันจะได้รับตัวอย่าง: ฉันต้องการรับ {"ชื่อ": "Bob", "อายุ": "17"}, {"ชื่อ": "Bob", "อายุ ":" 17 "}
Abdullah Al Mamun

10
นั่นเป็นเหตุผลที่คุณต้องเลื่อนอย่างต่อเนื่อง
Alejandro Bastidas

5
@AbdullahAlMamun คุณสามารถใช้แผนที่ใน. ฟิลเตอร์แทนการโทร. แผนที่ก่อนเช่นนี้:array.filter((value, index, self) => self.map(x => x.age).indexOf(value.age) == index)
Leo

1
ps:. แผนที่ภายใน. ตัวกรองอาจช้าดังนั้นควรระมัดระวังอาร์เรย์ขนาดใหญ่
Leo

2
@IvanNosov ตัวอย่างของคุณดีมากแม้ว่าคำอธิบายหลายบรรทัดจะทำงานอย่างไรกับพอยน์เตอร์ในการทำแผนที่และตัวกรองเอกสารจะทำให้มันสมบูรณ์แบบและชัดเจนสำหรับผู้เริ่มต้น
azakgaim

128

คุณสามารถใช้วิธีพจนานุกรมเช่นนี้ โดยทั่วไปคุณกำหนดค่าที่คุณต้องการให้ชัดเจนเป็นคีย์ใน "พจนานุกรม" (ที่นี่เราใช้อาร์เรย์เป็นวัตถุเพื่อหลีกเลี่ยงโหมดพจนานุกรม) หากไม่มีคีย์อยู่ให้เพิ่มค่านั้นเป็นค่าที่แตกต่างกัน

นี่คือตัวอย่างการทำงาน:

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
var unique = [];
var distinct = [];
for( let i = 0; i < array.length; i++ ){
  if( !unique[array[i].age]){
    distinct.push(array[i].age);
    unique[array[i].age] = 1;
  }
}
var d = document.getElementById("d");
d.innerHTML = "" + distinct;
<div id="d"></div>

นี่จะเป็น O (n) โดยที่ n คือจำนวนวัตถุในอาร์เรย์และ m คือจำนวนค่าที่ไม่ซ้ำกัน ไม่มีวิธีที่เร็วกว่า O (n) เพราะคุณต้องตรวจสอบแต่ละค่าอย่างน้อยหนึ่งครั้ง

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

ฉันได้อัปเดตโค้ดข้างต้นตามที่ระบุไว้อย่างไรก็ตามฉันได้อัปเดต jsperf เพื่อค้นหาวัตถุ 1,000 รายการแทน 3 3 มองข้ามข้อผิดพลาดด้านประสิทธิภาพจำนวนมากที่เกี่ยวข้อง ( ล้าสมัย jsperf )

ประสิทธิภาพ

https://jsperf.com/filter-vs-dictionary-more-dataเมื่อฉันเรียกใช้พจนานุกรมนี้เร็วขึ้น 96%

ตัวกรอง vs พจนานุกรม


4
รวดเร็วและไม่ต้องใช้ปลั๊กอินเพิ่มเติมใด ๆ เด็ดจริงๆ
mihai

2
วิธีการเกี่ยวกับif( typeof(unique[array[i].age]) == "undefined"){ distinct.push(array[i].age); unique[array[i].age] = 0; }
Timeless

7
ว้าวผลการดำเนินงานมีการเปลี่ยนแปลงในความโปรดปรานของตัวเลือกแผนที่ คลิกลิงก์ jsperf!
AT

1
@ 98percentmonkey - วัตถุถูกใช้เพื่ออำนวยความสะดวกในการ "เข้าหาพจนานุกรม" ภายในวัตถุแต่ละเหตุการณ์ที่ถูกติดตามจะถูกเพิ่มเป็นคีย์ (หรือถังขยะ) ด้วยวิธีนี้เมื่อเราเจอเหตุการณ์ที่เกิดขึ้นเราสามารถตรวจสอบเพื่อดูว่ามีกุญแจ (หรือถังขยะ); ถ้ามันมีอยู่เรารู้ว่าเหตุการณ์ไม่ซ้ำกัน - ถ้ามันไม่มีเรารู้ว่าเหตุการณ์นั้นไม่ซ้ำกันและเพิ่มเข้าไป
Travis J

1
@ 98percentmonkey - เหตุผลในการใช้วัตถุคือการค้นหาคือ O (1) เนื่องจากตรวจสอบชื่อคุณสมบัติหนึ่งครั้งในขณะที่อาร์เรย์คือ O (n) เนื่องจากต้องดูทุกค่าเพื่อตรวจสอบการมีอยู่ ในตอนท้ายของกระบวนการชุดของคีย์ (ถังขยะ) ในวัตถุแสดงถึงชุดของเหตุการณ์ที่ไม่ซ้ำกัน
เทรวิส J

91

นี่คือวิธีที่คุณจะแก้ปัญหานี้โดยใช้ชุดใหม่ผ่าน ES6 สำหรับ Typescript ณ วันที่ 25 สิงหาคม 2017

Array.from(new Set(yourArray.map((item: any) => item.id)))

3
สิ่งนี้ช่วยได้รับก่อนที่จะขอบคุณ Type 'Set' ไม่ใช่ประเภทอาเรย์
robert king

1
นี่คือคำตอบที่ดี จนกระทั่งฉันเลื่อนลงมาที่นี่ฉันจะเขียนคำตอบว่า: Object.keys (values.reduce <any> ((x, y) => {x [y] = null; return x;}, {} )); แต่คำตอบนี้กระชับและใช้กับทุกชนิดข้อมูลแทนที่จะเป็นแค่สตริง
jgosar

1
คำตอบนี้งดงาม!
lukeocodes

78

ด้วยการใช้คุณสมบัติ ES6 คุณสามารถทำสิ่งต่อไปนี้:

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];

6
วิธีการนี้จะทำงานกับประเภทดั้งเดิมเท่านั้น (ซึ่งเป็นกรณีที่นี่เนื่องจากอายุเป็นอาร์เรย์ของตัวเลข) แต่จะล้มเหลวสำหรับวัตถุ
k0pernikus

มีความคิดว่าจะทำให้มันทำงานกับวัตถุด้วยสองปุ่มได้อย่างไร?
cegprakash

1
มีบางอย่างที่เหมือนกันconst uniqueObjects = [ ...new Set( array.map( obj => obj.age) ) ].map( age=> { return array.find(obj => obj.age === age) } )
Harley B

62

ฉันแค่แผนที่และลบ dups:

var ages = array.map(function(obj) { return obj.age; });
ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; });

console.log(ages); //=> [17, 35]

แก้ไข: Aight! ไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุดในแง่ของประสิทธิภาพ แต่เป็น IMO ที่อ่านง่ายที่สุด ถ้าคุณสนใจเกี่ยวกับการเพิ่มประสิทธิภาพแบบไมโครหรือมีข้อมูลจำนวนมากการforวนรอบปกติจะมีประสิทธิภาพมากกว่า


3
@elclanrs - "ประสิทธิภาพมากที่สุดวิธีที่มีประสิทธิภาพ" - วิธีนี้เป็นวิธีที่ช้า
Travis J

1
@Eevee: ฉันเห็น รุ่นขีดล่างในคำตอบของคุณไม่เร็วอย่างใดอย่างหนึ่งในท้ายที่สุดคุณเลือกสิ่งที่สะดวกกว่าฉันสงสัย 1-30% มากขึ้นหรือน้อยลงสะท้อนถึง "การพัฒนาอย่างมาก" ในการทดสอบทั่วไปและเมื่อ OP / s อยู่ในหลักพัน .
elclanrs

4
ดีแน่นอน มีเพียงสามองค์ประกอบสิ่งที่เร็วที่สุดคือการสร้างตัวแปรสามตัวและใช้บางifตัว คุณจะได้ผลลัพธ์ที่แตกต่างกันมากเมื่อใช้สามล้าน
Eevee

1
ไม่เร็ว แต่ในกรณีของฉันมันใช้กลอุบายเพราะฉันไม่ได้จัดการกับชุดข้อมูลขนาดใหญ่ขอบคุณ
Felipe Alarcon

37
var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];

// or with lodash

var unique = _.uniq(_.map(array, 'age'));

ตัวอย่าง ES6

const data = [
  { name: "Joe", age: 17}, 
  { name: "Bob", age: 17}, 
  { name: "Carl", age: 35}
];

const arr = data.map(p => p.age); // [17, 17, 35]
const s = new Set(arr); // {17, 35} a set removes duplications, but it's still a set
const unique = [...s]; // [17, 35] Use the spread operator to transform a set into an Array
// or use Array.from to transform a set into an array
const unique2 = Array.from(s); // [17, 35]

6
ในขณะที่รหัสนี้อาจจะตอบคำถามให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและเหตุผลที่จะแก้ปัญหาที่เกิดขึ้นจะปรับปรุงคำตอบเป็นระยะยาวคุ้มค่า
Alexander

อันแรกที่ใช้ indexOf กับ index นั้นยอดเยี่ยมมาก
อนิจจา

โปรดทราบว่าสิ่งนี้ใช้ได้กับค่าดั้งเดิมเท่านั้น หากคุณมีอาร์เรย์ของวันที่คุณต้องมีวิธีการที่กำหนดเองเพิ่มเติม อ่านเพิ่มเติมได้ที่นี่: codeburst.io/javascript-array-distinct-5edc93501dc4
Molx

36

สำหรับผู้ที่ต้องการส่งคืนวัตถุที่มีคุณสมบัติทั้งหมดที่ไม่ซ้ำกันโดยคีย์

const array =
  [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
  ]

const key = 'age';

const arrayUniqueByKey = [...new Map(array.map(item =>
  [item[key], item])).values()];

console.log(arrayUniqueByKey);

   /*OUTPUT
       [
        { "name": "Bob", "age": 17 },
        { "name": "Carl", "age": 35 }
       ]
   */

 // Note: this will pick the last duplicated item in the list.


2
หมายเหตุ: นี่จะเลือกรายการที่ซ้ำกันครั้งสุดท้ายในรายการ
Eugene Kulabuhov

1
เพิ่มคำตอบนี้!
Arun Kumar Saini

1
สิ่งที่ฉันกำลังมองหา ขอบคุณที่แบ่งปันกัน!
Devner

1
ฉันคิดว่านี่น่าจะเป็นคำตอบสุดท้ายที่จะซื่อสัตย์!
Jaroslav Benc

26

มีคำตอบที่ถูกต้องอยู่แล้ว แต่ฉันต้องการเพิ่มคำตอบที่ใช้reduce()วิธีการเท่านั้น เพราะสะอาดและเรียบง่าย

function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) { a.push(d[prop]); }
    return a;
  }, []);
}

ใช้แบบนี้:

var array = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]

20

forEachรุ่นของคำตอบ @ เทรวิส-J ของ (ที่เป็นประโยชน์ในเบราว์เซอร์ที่ทันสมัยและโหนด JS โลก):

var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});

เร็วขึ้น 34% สำหรับ Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3

และวิธีแก้ปัญหาทั่วไปที่ใช้ฟังก์ชั่นตัวทำแผนที่ (ช้ากว่าแผนที่โดยตรง แต่คาดว่า):

function uniqueBy(arr, fn) {
  var unique = {};
  var distinct = [];
  arr.forEach(function (x) {
    var key = fn(x);
    if (!unique[key]) {
      distinct.push(key);
      unique[key] = true;
    }
  });
  return distinct;
}

// usage
uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]

1
ฉันชอบวิธีการแก้ปัญหาทั่วไปที่ดีที่สุดเพราะมันไม่น่าเป็นไปได้ว่าอายุเป็นค่าที่แตกต่างเพียงอย่างเดียวที่จำเป็นในสถานการณ์โลกแห่งความจริง
Toft

5
ในแบบทั่วไปเปลี่ยน "ชัดเจน
สิลาสแฮนเซน

15

ฉันได้เริ่มขีดเส้นใต้ในโครงการใหม่ทั้งหมดโดยค่าเริ่มต้นดังนั้นฉันจึงไม่ต้องคิดเกี่ยวกับปัญหาการบันทึกข้อมูลเหล่านี้

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());

[17, 35]ผลิต


13

นี่เป็นอีกวิธีในการแก้ปัญหานี้:

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);

ฉันไม่รู้เลยว่าวิธีนี้เร็วกว่าเมื่อเทียบกับวิธีอื่น แต่ฉันชอบรูปลักษณ์ที่ดูสะอาดตากว่า ;-)


แก้ไข: เอาล่ะข้างต้นดูเหมือนจะเป็นทางออกที่ช้าที่สุดของทั้งหมดที่นี่

ฉันได้สร้างกรณีทดสอบประสิทธิภาพที่นี่: http://jsperf.com/distinct-values-from-array

แทนที่จะทดสอบหาอายุ (จำนวนเต็ม) ฉันเลือกที่จะเปรียบเทียบชื่อ (Strings)

วิธีที่ 1 (การแก้ปัญหาของ TS) นั้นรวดเร็วมาก น่าสนใจพอวิธีที่ 7 มีประสิทธิภาพเหนือกว่าโซลูชันอื่น ๆ ทั้งหมดที่นี่ฉันเพิ่งลบ. indexOf () และใช้การดำเนินการแบบ "ทำด้วยมือ" ของมันหลีกเลี่ยงการเรียกฟังก์ชันแบบวนซ้ำ:

var result = [];
loop1: for (var i = 0; i < array.length; i++) {
    var name = array[i].name;
    for (var i2 = 0; i2 < result.length; i2++) {
        if (result[i2] == name) {
            continue loop1;
        }
    }
    result.push(name);
}

ความแตกต่างของประสิทธิภาพในการใช้ Safari & Firefox นั้นน่าทึ่งและดูเหมือนว่า Chrome จะทำงานได้ดีที่สุดในการเพิ่มประสิทธิภาพ

ฉันไม่แน่ใจว่าทำไมตัวอย่างด้านบนจึงเร็วเมื่อเปรียบเทียบกับคนอื่นบางทีคนที่ฉลาดกว่าฉันก็มีคำตอบ ;-)


9

ใช้Lodash

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]

2
คุณสามารถอธิบายวิธีการทำเช่นนี้กับจาวาสคริปต์ธรรมดาที่เกี่ยวข้องกับคำถามได้หรือไม่?
Ruskin

มันเป็นฟังก์ชั่น _.chain ขีด
Vini

2
แต่ถอนไม่ได้อยู่ในบ้านพัก
Yash Vekaria

1
_.chain (อาร์เรย์) .map (อายุ) ที่ไม่ซ้ำกัน () มูลค่า ()..; ทำงานให้ฉัน
Yash Vekaria

เวอร์ชัน 4.0 มีการเปลี่ยนแปลงที่เปลี่ยนแปลงอยู่บ้าง - stackoverflow.com/a/31740263/4050261
Adarsh ​​Madrecha


6
function get_unique_values_from_array_object(array,property){
    var unique = {};
    var distinct = [];
    for( var i in array ){
       if( typeof(unique[array[i][property]]) == "undefined"){
          distinct.push(array[i]);
       }
       unique[array[i][property]] = 0;
    }
    return distinct;
}


5

ต่อไปนี้เป็นโซลูชันที่ใช้งานได้หลากหลายซึ่งใช้การลดอนุญาตให้ทำแผนที่และรักษาลำดับการแทรก

รายการ : อาร์เรย์

mapper : ฟังก์ชัน unary ที่แมปไอเท็มกับเกณฑ์หรือว่างเพื่อแมปไอเท็มเอง

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

การใช้

const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);

คุณสามารถเพิ่มสิ่งนี้ลงในต้นแบบ Array ของคุณและปล่อยให้พารามิเตอร์รายการนั้นเป็นสไตล์ของคุณ ...

const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;

นอกจากนี้คุณยังสามารถใช้ชุดแทนที่จะเป็นอาร์เรย์เพื่อเพิ่มความเร็วในการจับคู่

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}

5

const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => accumulator.some(x => x.id === current.id)? accumulator: [...accumulator, current ], []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/


2
ดูเหมือนว่า O (n ^ 2)
cegprakash

4

เพิ่งพบสิ่งนี้และฉันคิดว่ามันมีประโยชน์

_.map(_.indexBy(records, '_id'), function(obj){return obj})

ใช้ขีดล่างอีกครั้งดังนั้นหากคุณมีวัตถุเช่นนี้

var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]

มันจะให้คุณวัตถุที่เป็นเอกลักษณ์เท่านั้น

สิ่งที่เกิดขึ้นที่นี่คือการindexByส่งคืนแผนที่เช่นนี้

{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }

และเพียงเพราะมันเป็นแผนที่ปุ่มทุกปุ่มจึงมีเอกลักษณ์

จากนั้นฉันก็แค่จับคู่รายการนี้กลับไปยังอาร์เรย์

ในกรณีที่คุณต้องการเฉพาะค่าที่แตกต่าง

_.map(_.indexBy(records, '_id'), function(obj,key){return key})

โปรดทราบว่าการkeyส่งคืนเป็นสตริงดังนั้นหากคุณต้องการจำนวนเต็มแทนคุณควรทำ

_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})

4

ฉันคิดว่าคุณกำลังมองหาฟังก์ชั่น groupBy (ใช้ Lodash)

_personsList = [{"name":"Joe", "age":17}, 
                {"name":"Bob", "age":17}, 
                {"name":"Carl", "age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);

สร้างผลลัพธ์:

17,35

การสาธิต jsFiddle: http://jsfiddle.net/4J2SX/201/


3

ถ้าชอบฉันคุณชอบ "ฟังก์ชั่น" มากกว่าโดยไม่ลดทอนความเร็วตัวอย่างนี้ใช้การค้นหาพจนานุกรมที่รวดเร็วภายในห่อปิดลด

var array = 
[
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
var uniqueAges = array.reduce((p,c,i,a) => {
    if(!p[0][c.age]) {
        p[1].push(p[0][c.age] = c.age);
    }
    if(i<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

จากการทดสอบนี้ทางออกของฉันเร็วเป็นสองเท่าของคำตอบที่เสนอ



3

ฉันรู้ว่าโค้ดของฉันมีความยาวน้อยและซับซ้อนเล็กน้อย แต่ก็เข้าใจได้ดังนั้นฉันจึงลองใช้วิธีนี้

ฉันพยายามพัฒนาฟังก์ชั่นต้นแบบที่นี่และรหัสก็เปลี่ยนไป

Distinct เป็นฟังก์ชั่นต้นแบบของฉันเอง

<script>
  var array = [{
      "name": "Joe",
      "age": 17
    },
    {
      "name": "Bob",
      "age": 17
    },
    {
      "name": "Carl",
      "age": 35
    }
  ]

  Array.prototype.Distinct = () => {
    var output = [];
    for (let i = 0; i < array.length; i++) {
      let flag = true;
      for (let j = 0; j < output.length; j++) {
        if (array[i].age == output[j]) {
          flag = false;
          break;
        }
      }
      if (flag)
        output.push(array[i].age);
    }
    return output;
  }
  //Distinct is my own function
  console.log(array.Distinct());
</script>


2

หากคุณมี Array.prototype.includes หรือยินดีที่จะpolyfillมันจะทำงานได้:

var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });

1

รหัสด้านล่างของฉันจะแสดงอาร์เรย์อายุที่ไม่ซ้ำกันรวมถึงอาร์เรย์ใหม่ที่ไม่มีอายุซ้ำกัน

var data = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.push(value.age);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```

1

ฉันเขียนของฉันเองใน TypeScript สำหรับกรณีทั่วไปเช่นนั้นใน Kotlin's Array.distinctBy {}...

function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}

แน่นอนอยู่ที่ไหนUhashable สำหรับวัตถุคุณอาจต้องhttps://www.npmjs.com/package/es6-json-stable-stringify


1

ในกรณีที่คุณต้องการเฉพาะวัตถุทั้งหมด

const _ = require('lodash');

var objects = [
  { 'x': 1, 'y': 2 },
  { 'y': 1, 'x': 2 },
  { 'x': 2, 'y': 1 },
  { 'x': 1, 'y': 2 }
];

_.uniqWith(objects, _.isEqual);

[Object {x: 1, y: 2}, Object {x: 2, y: 1}]


downvoter: จิตใจอธิบายว่าทำไม downvote
cegprakash

1

การตอบคำถามเก่านี้ไร้ประโยชน์ แต่มีคำตอบง่ายๆที่พูดถึงลักษณะของ Javascript วัตถุใน Javascript เป็นตารางแฮชโดยเนื้อแท้ เราสามารถใช้ปุ่มนี้เพื่อรับรหัสพิเศษ:

var o = {}; array.map(function(v){ o[v.age] = 1; });

จากนั้นเราสามารถลดแฮชไปเป็นอาร์เรย์ของค่าที่ไม่ซ้ำกัน:

var a2 = []; for (k in o){ a2.push(k); }

นั่นคือทั้งหมดที่คุณต้องการ อาร์เรย์a2มีเพียงช่วงอายุที่ไม่ซ้ำกัน


1

เรียบง่ายหนึ่งซับด้วยประสิทธิภาพที่ยอดเยี่ยม 6% เร็วกว่าการแก้ ES6 ฉันในการทดสอบ

var ages = array.map(function(o){return o.age}).filter(function(v,i,a) {
    return a.indexOf(v)===i
});

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

2
array.map( o => o.age).filter( (v,i,a) => a.indexOf(v)===i)ด้วยฟังก์ชั่ลูกศร: ฉันใช้คำหลักฟังก์ชั่นน้อยมากตอนนี้ฉันต้องอ่านสองครั้งเมื่อฉันเห็นมัน😊
Drenai
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.