Loại bỏ các bản sao tạo thành một mảng


100

Tôi có một mảng các đối tượng trông giống như sau:

var array = [
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
    ...
];

Như bạn có thể thấy, một số tên được lặp lại. Tôi muốn nhận một mảng mới chỉ có tên, nhưng nếu một số tên lặp lại, tôi không muốn thêm lại. Tôi muốn mảng này:

var newArray = ["Name1", "Name2"];

Tôi đang cố gắng làm điều này với map:

var newArray = array.map((a) => {
    return a.name;
});

Nhưng vấn đề là điều này trả về:

newArray = ["Name1", "Name1", "Name2", "Name2"];

Làm thế nào tôi có thể đặt một số điều kiện bên trong map, vì vậy nó sẽ không trả về một phần tử đã tồn tại? Tôi muốn thực hiện việc này với maphoặc một số tính năng ECMAScript 5 hoặc ECMAScript 6 khác.


2
Sau đó, loại bỏ các bản sao khỏi mảng.
Tushar


1
bản sao có thể có của Xóa bản sao khỏi mảng JavaScript
Bergi

Tại sao dòng chứa id: 125 không kết thúc bằng dấu phẩy?
Peter Mortensen,

Xin lưu ý rằng tất cả các câu trả lời sử dụng .indexOf () sẽ có hiệu suất kém nếu mảng lớn, do độ phức tạp về thời gian bậc hai của chúng . Tôi khuyên bạn nên sử dụng Bộ ES6 hoặc sử dụng một đối tượng ES5 làm từ điển.
joeytwiddle

Câu trả lời:


210

Với ES6, bạn có thể sử dụng Setcho các giá trị duy nhất, sau khi chỉ ánh xạ tên của các đối tượng.

Đề xuất này sử dụng cú pháp lây lan... để thu thập các mục trong một mảng mới.

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      names = [...new Set(array.map(a => a.name))];

console.log(names);


9
Để biết thông tin của tôi .. ...làm gì?
Rajshekar Reddy

9
@RajshekarReddy, đó là một cú pháp lây lan... để xây dựng một mảng với một đối tượng có thể lặp lại.
Nina Scholz

5
Tôi không ủng hộ bản thân "Set", mà là để sử dụng thông minh toán tử spread. Thật tuyệt khi học được điều gì đó mới hoặc thấy điều gì đó mới được áp dụng vào một ví dụ thực tế, vì vậy bài đăng này xứng đáng nhận được sự ủng hộ.
briosheje

33
Đó chỉ là ý kiến ​​cá nhân của tôi, nhưng là một ý kiến khá phổ biến . Việc sử dụng Array.fromtruyền đạt mục đích chuyển đổi tốt hơn nhiều (và cũng dễ hiểu hơn / dễ tìm kiếm hơn cho người mới), cú pháp lây lan chỉ nên được sử dụng khi phần tử lây lan là một trong nhiều. Có lẽ gọi nó là “hủ tục” thì hơi quá, xin lỗi, tôi chỉ mong đừng để nó trở thành một thành ngữ chung.
Bergi

3
@KRyan ES6 có mới như vậy không? Nó đã được hoàn thành vào tháng 6 năm 2015. Đó là thời gian để bắt đầu tìm hiểu và sử dụng các tính năng mới
edc65

64

Nếu bạn đang tìm kiếm một giải pháp JavaScript không phải là ES 6 (không có Bộ), bạn có thể sử dụng phương thức của Arrayreduce :

var array=[
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];
var names = array.reduce(function (a, b) {
  if (a.indexOf(b.name) == -1) {
    a.push(b.name)
  }
  return a;
}, []);

console.log(names);


2
Câu trả lời này cũng có lợi ích là nó không yêu cầu các mảng hoặc tập hợp trung gian, đặc biệt là những mảng chỉ chứa bản thân tên. (Nó đi trực tiếp từ mảng các đối tượng để mảng của các đối tượng.) 1
jpmc26

@Iwrestledabearonce, không phải tham số thứ hai này là đối tượng giống với đối tượng được trả về?
Kaiido

Đúng, nhưng nó không đi thẳng từ cái này sang cái kia như sắp xếp hoặc bộ lọc, nó được xây dựng lại từ một mảng trống.
Tôi đã đấu vật với một con gấu một lần.

1
@Dekel - Phiên bản nhỏ hơn :)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
Rayon

3
@Rayon Xin đừng quá chú ý đến kích thước của mã nguồn, đây là lỗi chung của nhiều lập trình viên tự làm, quan trọng nhất là chương trình của bạn phải hiệu quả và dễ đọc.

16

Cá nhân tôi không hiểu tại sao mọi người lại ưa thích ES 6. Nếu đó là mã của tôi, tôi muốn hỗ trợ càng nhiều trình duyệt càng tốt.

var array=[
{id:123, value:"value1", name:"Name1"},
{id:124, value:"value2", name:"Name1"},
{id:125, value:"value3", name:"Name2"},
{id:126, value:"value4", name:"Name2"}
];

   // Create array of unique names
var a = (function(a){
  for (var i = array.length; i--;)
    if (a.indexOf(array[i].name) < 0) a.push(array[i].name);
  return a;
})([]);

console.log(a);


2
Sẽ hiệu quả hơn khi sử dụng một đối tượng làm từ điển thay vì indexOf-ing mọi lúc? Ví dụ: làm added[name] = trueif(added[name])thay vì if(a.indexOf(name)).
Bojidar Marinov

7
"cá nhân tôi không hiểu tại sao mọi người đều ưa thích es6" tại sao bạn lại tán thành a goto fail, làm rò rỉ biến chỉ mục của bạn vào phạm vi bao quanh, v.v. một phần cực kỳ dễ dàng để polyfill / chuyển đổi cho các trình duyệt cũ hơn.
Jared Smith

5
Đọc nhiều hơn? Đó là một vòng lặp kỳ quái để đẩy các mục vào một mảng. Không thể đọc được nhiều hơn thế ......
Tôi đã đấu vật với một con gấu một lần.

4
Thành thật mà nói, bạn có cảm giác như đang cố gắng quá sức để tìm ra điều gì đó để phàn nàn.
Tôi đã đấu vật với một con gấu một lần.

2
"Tôi đang đề cập đến lỗi SSL nổi tiếng của Apple" - Nó không nổi tiếng đến mức bạn có thể đi và tham khảo nó trong các bình luận ngẫu nhiên ngoài ngữ cảnh và mong mọi người nhận ra nó.
Hejazzman

14

Bạn cũng có thể chỉ cần kết hợp mapvớifilter

var array = [
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];

var unique = array
  .map( item => item.name )
  .filter( ( item, idx, arr ) => arr.indexOf( item ) == idx ) 

console.log(unique)


11

Bạn có thể sử dụng Object.keys () để lấy mảng tên thuộc tính có thể liệt kê của một đối tượng nhất định từ kết quả đối tượng của arraybiến lặp với Array.prototype.reduce () trong đó các khóa là tên bị hủy

Mã:

const array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}],
      names = Object.keys(array.reduce((a, { name }) => (a[name] = 1, a), {}))

console.log(names)


7

Nhiều câu trả lời hay ở đây. Tôi chỉ muốn đóng góp với một số đa dạng với hy vọng cung cấp cho bạn một góc nhìn khác.

Mảng thuộc loại đối tượng trong JavaScript, vì vậy chúng có thể được sử dụng như một hàm băm cùng một lúc. Bằng cách sử dụng chức năng này, chúng ta có thể đơn giản hóa công việc cần thực hiện chỉ trong một thao tác giảm thiểu với độ phức tạp thời gian là O (n).

Nếu bạn không hài lòng với việc mảng của mình giữ một số thuộc tính khác với các khóa mảng, bạn có thể cân nhắc giữ một đối tượng băm riêng biệt.

var array = [{id:123, value:"value1", name:"Name1"},
             {id:124, value:"value2", name:"Name1"},
             {id:125, value:"value3", name:"Name2"},
             {id:126, value:"value4", name:"Name2"}
            ],
result = array.reduce((p,c) => p[c.name] ? p : (p[c.name] = true, p.push(c.name), p), []);
console.log(result);


Giải pháp ES5 này có hiệu suất tốt, nhưng việc sử dụng mảng cho hai mục đích sẽ gây nhầm lẫn và nguy hiểm nếu một trong các tên chỉ là một số. Tôi khuyên bạn nên làm những p[c.name]thứ trên một đối tượng riêng biệt ovà sau đó loại bỏ nó sau đó.
joeytwiddle

7

Tôi đồng ý rằng nếu bạn chỉ cần các namegiá trị, a Setlà con đường để đi.

Tuy nhiên , nếu bạn muốn lấy một mảng các đối tượng duy nhất dựa trên thuộc nametính, tôi khuyên bạn nên sử dụng a Map. Một cách nhanh chóng để tạo một Bản đồ, là thông qua một loạt các [key, value]mảng:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      unique = new Map(array.map(obj => [obj.name, obj]));

// To get the unique objects
const uniques = Array.from(unique.values());

// Get the names like you already did:
console.log("Names:", uniques.map(obj => obj.name));

// If you ever need the complete array of unique objects, you got a ref:
console.log(JSON.stringify(uniques));
.as-console-wrapper { min-height: 100%; }

Một lợi ích bổ sung của Mapnó là bạn có được cả filterchức năng cắt bỏ những thứ không phải là duy nhất mà không làm mất kết nối với các đối tượng nguồn. Tất nhiên, nó chỉ cần thiết nếu bạn cần tham chiếu nhóm đối tượng duy nhất nhiều lần.



2

Với ES6, điều này sẽ thực hiện công việc.

var array=[
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
];

var set = new Set();

array.forEach((a)=>{
    set.add(a.name);
}); 

console.log(Array.from(set));


11
mapđược cho là sử dụng với chức năng thuần túy, không có tác dụng phụ. Đây sẽ là một công việc cho forEachhoặc reduce.
Vincent van der Weele

1

Sử dụng UnderscoreJS,

array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}];
get_names =  _.pluck(_.uniq(array, 'name'), 'name')
console.log(get_names)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

`


0

Trong ES5, sử dụng một đối tượng làm từ điển cho hiệu suất O ( n ).

Điều này sẽ chỉ hoạt động nếu tất cả các khóa là Chuỗi.

var array = [
    {id: 123, value: "value1", name: "Name1"},
    {id: 124, value: "value2", name: "Name1"},
    {id: 125, value: "value3", name: "Name2"},
    {id: 126, value: "value4", name: "Name2"}
];

var allNames = array.map(item => item.name);

var map = {};
allNames.forEach(name => {
  map[name] = true;
});
var uniqueNames = Object.keys(map);

console.log(uniqueNames);

Bạn có thể làm điều tương tự trong một biểu thức nếu bạn thích:

var uniqueNames = Object.keys(allNames.reduce((m, n) => (m[n] = true, m), {}));

nhưng tôi thấy biểu mẫu mệnh lệnh dễ đọc hơn.


Tôi đã sử dụng các hàm mũi tên của ES6 để rõ ràng. Nếu bạn thực sự đang nhắm mục tiêu ES5 mà không có trình chuyển tiếp, thì bạn sẽ cần sử dụng biểu mẫu đầy đủ để thay thế:function (item) { return item.name; }
joeytwiddle

Để thay thế Object.keys(), câu trả lời này chỉ sử dụng filter()để giữ lại những cái tên chưa được lưu trữ trong bản đồ, khá thanh lịch.
joeytwiddle


0

Sử dụng array#forEach()array#indexOf()các phương pháp như thế này nếu bạn muốn có khả năng tương thích tối đa , cú pháp ngắn gọn:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }]

// initialize an empty array named names
let names = [];

// iterate through every element of `array` & check if it's 'name' key's value already in names array if not ADD it 
array.forEach(function(element) { if (names.indexOf(element.name) === -1) names.push(element.name) });
// or use tilde like this:
//array.forEach(function(element) { if (~names.indexOf(element.name)) names.push(element.name) });

console.log(names);

Tuy nhiên, nếu tính tương thích không phải là vấn đề, hãy sử dụng đối tượng của ECMAScript 6 và các phương thức như sau: Setarray#mapArray.from()

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }];

// iterate through every element from array using map and store it in Set(a Set won't have duplicates) then convert the Set back to Array(using Array.from)
let names = Array.from(new Set(array.map(element => element.name)));

console.log(names);


0

Đó là cách tôi đã làm, sử dụng một mảng trống riêng biệt.

var array = [
   {id:123, value:"value1", name:"Name1"},
   {id:124, value:"value2", name:"Name1"},
   {id:125, value:"value3", name:"Name2"},
   {id:126, value:"value4", name:"Name2"}	    
];

var array2 = []		
		
for (i=0; i<array.length;i++){						
   if (array2.indexOf(array[i].name) == -1){				
     array2.push(array[i].name);
    }
}			

console.log(array2)	


-1

Dành cho những ai đang tìm kiếm 1 lớp lót

const names = array.reduce((acc, {name}) => acc.includes(name) ? acc : [name, ...acc], []);

hoặc không sử dụng các phương thức trên nguyên mẫu của mảng

const { reduce, includes } = Array;
const names = reduce(array, (acc, {name}) => includes(acc, name) ? acc : [name, ...acc], []);

có thể hữu ích để viết một số hàm thuần túy để giải quyết vấn đề này

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);

-1
var __array=[{id:123, value:"value1", name:"Name1"},{id:124, value:"value2", name:"Name1"},{id:125, value:"value3", name:"Name2"},{id:126, value:"value4", name:"Name2"}];

function __checkArray(__obj){
    var flag = true;
    for(let i=0; i < __array.length; i++){
        if(__obj.id == __array.id){
            flag = false;
            break;
        }
    }

    return flag;
}

var __valToPush = {id: 127, value: "value5", name: "Name3"};
if(__checkArray(__valToPush)){
    __array.push(__valToPush)
}

11
bạn có lý do gì đặc biệt để sử dụng quá nhiều dấu gạch dưới không?
Nina Scholz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.