Cách tạo bộ lọc lặp lại ra kết quả trùng lặp


131

Tôi đang chạy đơn giản ng-repeat tệp JSON và muốn lấy tên danh mục. Có khoảng 100 đối tượng, mỗi đối tượng thuộc một danh mục - nhưng chỉ có khoảng 6 danh mục.

Mã hiện tại của tôi là:

<select ng-model="orderProp" >
  <option ng-repeat="place in places" value="{{place.category}}">{{place.category}}</option>
</select>

Đầu ra là 100 tùy chọn khác nhau, chủ yếu là trùng lặp. Làm cách nào để sử dụng Angular để kiểm tra xem {{place.category}}đã tồn tại chưa và không tạo tùy chọn nếu nó đã ở đó?

chỉnh sửa: Trong javascript của tôi $scope.places = JSON data, chỉ để làm rõ


1
Tại sao bạn không chỉ trích $ scope.places của mình? sử dụng bản đồ jquery api.jquery.com/map
anazimok

giải pháp làm việc cuối cùng của bạn là gì? IS ở trên hoặc có một số JS đang thực hiện việc sao chép
mark1234

Tôi muốn biết giải pháp cho việc này. Xin vui lòng gửi theo dõi. Cảm ơn!
jdstein1

@ jdstein1 Tôi sẽ bắt đầu với TLDR: Sử dụng các câu trả lời bên dưới hoặc sử dụng vanilla Javascript để chỉ lọc các giá trị duy nhất trong một mảng. Những gì tôi đã làm: Cuối cùng, đó là một vấn đề với logic và sự hiểu biết của tôi về MVC. Tôi đã tải dữ liệu từ MongoDB yêu cầu một đống dữ liệu và muốn Angular lọc nó một cách kỳ diệu xuống những nơi duy nhất. Giải pháp, như thường lệ, là ngừng lười biếng và sửa mô hình DB của tôi - đối với tôi, nó đang gọi Mongo db.collection.distinct("places"), điều này tốt hơn nhiều so với thực hiện trong Angular! Đáng buồn thay, điều này sẽ không làm việc cho tất cả mọi người.
JVG

Cảm ơn các cập nhật!
jdstein1

Câu trả lời:


142

Bạn có thể sử dụng bộ lọc duy nhất từ AngularUI (mã nguồn có sẵn ở đây: Bộ lọc duy nhất AngularUI ) và sử dụng trực tiếp trong các tùy chọn ng (hoặc ng-repeat).

<select ng-model="orderProp" ng-options="place.category for place in places | unique:'category'">
    <option value="0">Default</option>
    // unique options from the categories
</select>

33
Đối với những người không thể có bộ lọc duy nhất của AngularUI hoạt động: các bộ lọc nằm trong một mô-đun riêng: Ví dụ: bạn phải đưa nó làm tài liệu tham khảo bổ sung trong mô-đun của mình angular.module('yourModule', ['ui', 'ui.filters']);. Đã bị bối rối cho đến khi tôi nhìn vào tập tin js AngularUI.
GFoley83

8
Bộ uniquelọc hiện có thể được tìm thấy như một phần của Utular UI Utils
Nick

2
Trong phiên bản mới của ui utils, bạn chỉ có thể bao gồm ui.unique. sử dụng cài đặt Bower chỉ cho các mô-đun.
Học thống kê bằng ví dụ

Nếu bạn không muốn bao gồm AngularUI và toàn bộ, nhưng muốn sử dụng bộ lọc duy nhất, bạn có thể sao chép dán nguồn unique.js vào ứng dụng của mình, sau đó đổi angular.module('ui.filters')thành tên ứng dụng của bạn.
chakena

37

Hoặc bạn có thể viết bộ lọc của riêng bạn bằng cách sử dụng lodash.

app.filter('unique', function() {
    return function (arr, field) {
        return _.uniq(arr, function(a) { return a[field]; });
    };
});

Xin chào Mike, trông rất thanh lịch nhưng dường như không hoạt động như mong đợi khi tôi vượt qua một bộ lọc như duy nhất: trạng thái [tên trường của tôi] nó chỉ trả về kết quả đầu tiên trái ngược với danh sách mong muốn của tất cả các bức tượng duy nhất có sẵn. Có ai đoán được tại sao không?
SinSync

3
Mặc dù tôi đã sử dụng dấu gạch dưới, giải pháp này hoạt động như một bùa mê. Cảm ơn!
Jon Black

Giải pháp rất thanh lịch.
JD Smith

1
lodash là một ngã ba của gạch dưới. Nó có hiệu suất tốt hơn và nhiều tiện ích hơn.
Mike Ward

2
trong lodash v4 _.uniqBy(arr, field);nên hoạt động cho các thuộc tính lồng nhau
ijavid

30

Bạn có thể sử dụng bộ lọc 'unique' (aliases: uniq) trong mô-đun angular.filter

cách sử dụng: colection | uniq: 'property'
bạn cũng có thể lọc theo các thuộc tính lồng nhau: colection | uniq: 'property.nested_property'

Những gì bạn có thể làm, là một cái gì đó như thế ..

function MainController ($scope) {
 $scope.orders = [
  { id:1, customer: { name: 'foo', id: 10 } },
  { id:2, customer: { name: 'bar', id: 20 } },
  { id:3, customer: { name: 'foo', id: 10 } },
  { id:4, customer: { name: 'bar', id: 20 } },
  { id:5, customer: { name: 'baz', id: 30 } },
 ];
}

HTML: Chúng tôi lọc theo id khách hàng, nghĩa là xóa khách hàng trùng lặp

<th>Customer list: </th>
<tr ng-repeat="order in orders | unique: 'customer.id'" >
   <td> {{ order.customer.name }} , {{ order.customer.id }} </td>
</tr>

kết quả
Danh sách khách hàng:
foo 10
bar 20
baz 30


giống như câu trả lời của bạn, nhưng có một thời gian khó dịch nó cho vấn đề của tôi. Làm thế nào bạn sẽ xử lý một danh sách lồng nhau. Ví dụ: danh sách các đơn hàng có chứa danh sách các mặt hàng cho mỗi đơn hàng. Trong ví dụ của bạn, bạn có một khách hàng mỗi đơn hàng. Tôi muốn xem một danh sách duy nhất các mặt hàng trên tất cả các đơn đặt hàng. Không tin vào quan điểm, nhưng bạn cũng có thể nghĩ về nó như một khách hàng có nhiều đơn đặt hàng và một đơn hàng có nhiều mặt hàng. Làm cách nào tôi có thể hiển thị tất cả các mặt hàng trước đây mà khách hàng đã đặt hàng? Suy nghĩ?
Greg Grater

@GregGrater Bạn có thể cung cấp đối tượng ví dụ tương tự như vấn đề của bạn không?
a8m

Cảm ơn @Ariel M.! Đây là một ví dụ về dữ liệu:
Greg Grater

Với phiên bản góc nào thì nó tương thích?
Icet

15

mã này làm việc cho tôi.

app.filter('unique', function() {

  return function (arr, field) {
    var o = {}, i, l = arr.length, r = [];
    for(i=0; i<l;i+=1) {
      o[arr[i][field]] = arr[i];
    }
    for(i in o) {
      r.push(o[i]);
    }
    return r;
  };
})

và sau đó

var colors=$filter('unique')(items,"color");

2
Định nghĩa của l nên là l = Array! = Không xác định? Array.length: 0 vì nếu không có lỗi phân tích cú pháp trong angularjs
Gerrit

6

Nếu bạn muốn liệt kê các danh mục, tôi nghĩ bạn nên nói rõ ý định của mình trong chế độ xem.

<select ng-model="orderProp" >
  <option ng-repeat="category in categories"
          value="{{category}}">
    {{category}}
  </option>
</select>

trong bộ điều khiển:

$scope.categories = $scope.places.reduce(function(sum, place) {
  if (sum.indexOf( place.category ) < 0) sum.push( place.category );
  return sum;
}, []);

bạn có thể giải thích điều này nhiều hơn một chút Tôi muốn lặp lại các yếu tố thứ liên tiếp cho mỗi danh mục duy nhất. Có phải JS chỉ đi trong tệp JS nơi trình điều khiển được xác định?
mark1234

Nếu bạn muốn nhóm các tùy chọn, đó là câu hỏi khác nhau. Bạn có thể muốn xem xét yếu tố optgroup. Angular hỗ trợ optgroup. tìm kiếm group bybiểu thức trong selectchỉ thị
Tosh

Sẽ xảy ra trong trường hợp một địa điểm được thêm / xóa? Danh mục liên quan sẽ được thêm / xóa nếu đó là trường hợp duy nhất ở những nơi?
Greg Grater

4

Đây là một ví dụ đơn giản và chung chung.

Bộ lọc:

sampleApp.filter('unique', function() {

  // Take in the collection and which field
  //   should be unique
  // We assume an array of objects here
  // NOTE: We are skipping any object which
  //   contains a duplicated value for that
  //   particular key.  Make sure this is what
  //   you want!
  return function (arr, targetField) {

    var values = [],
        i, 
        unique,
        l = arr.length, 
        results = [],
        obj;

    // Iterate over all objects in the array
    // and collect all unique values
    for( i = 0; i < arr.length; i++ ) {

      obj = arr[i];

      // check for uniqueness
      unique = true;
      for( v = 0; v < values.length; v++ ){
        if( obj[targetField] == values[v] ){
          unique = false;
        }
      }

      // If this is indeed unique, add its
      //   value to our values and push
      //   it onto the returned array
      if( unique ){
        values.push( obj[targetField] );
        results.push( obj );
      }

    }
    return results;
  };
})

Đánh dấu:

<div ng-repeat = "item in items | unique:'name'">
  {{ item.name }}
</div>
<script src="your/filters.js"></script>

Điều này hoạt động tốt nhưng nó gây ra lỗi trong bảng điều khiển Cannot read property 'length' of undefinedtrong dòngl = arr.length
Soul Eeater

4

Tôi quyết định mở rộng câu trả lời của @ thethakuri để cho phép bất kỳ chiều sâu nào cho thành viên duy nhất. Đây là mã. Điều này dành cho những người không muốn bao gồm toàn bộ mô-đun AngularUI chỉ cho chức năng này. Nếu bạn đã sử dụng AngularUI, hãy bỏ qua câu trả lời này:

app.filter('unique', function() {
    return function(collection, primaryKey) { //no need for secondary key
      var output = [], 
          keys = [];
          var splitKeys = primaryKey.split('.'); //split by period


      angular.forEach(collection, function(item) {
            var key = {};
            angular.copy(item, key);
            for(var i=0; i<splitKeys.length; i++){
                key = key[splitKeys[i]];    //the beauty of loosely typed js :)
            }

            if(keys.indexOf(key) === -1) {
              keys.push(key);
              output.push(item);
            }
      });

      return output;
    };
});

Thí dụ

<div ng-repeat="item in items | unique : 'subitem.subitem.subitem.value'"></div>

2

Tôi đã có một chuỗi các chuỗi, không phải các đối tượng và tôi đã sử dụng phương pháp này:

ng-repeat="name in names | unique"

với bộ lọc này:

angular.module('app').filter('unique', unique);
function unique(){
return function(arry){
        Array.prototype.getUnique = function(){
        var u = {}, a = [];
        for(var i = 0, l = this.length; i < l; ++i){
           if(u.hasOwnProperty(this[i])) {
              continue;
           }
           a.push(this[i]);
           u[this[i]] = 1;
        }
        return a;
    };
    if(arry === undefined || arry.length === 0){
          return '';
    }
    else {
         return arry.getUnique(); 
    }

  };
}

2

CẬP NHẬT

Tôi đã giới thiệu lại việc sử dụng Set nhưng xin lỗi, điều này không hoạt động đối với ng-repeat, cũng như Map vì ng-repeat chỉ hoạt động với mảng. Vì vậy, bỏ qua câu trả lời này. Dù sao, nếu bạn cần lọc ra các bản sao một cách như cách khác đã nói angular filters, đây là liên kết cho phần bắt đầu .


Câu trả lời cũ

Bạn có thể sử dụng cấu trúc dữ liệu Set tiêu chuẩn ECMAScript 2015 (ES6) , thay vì Cấu trúc dữ liệu mảng theo cách này bạn lọc các giá trị lặp lại khi thêm vào Set. (Nhớ các bộ không cho phép lặp lại các giá trị). Thực sự dễ sử dụng:

var mySet = new Set();

mySet.add(1);
mySet.add(5);
mySet.add("some text");
var o = {a: 1, b: 2};
mySet.add(o);

mySet.has(1); // true
mySet.has(3); // false, 3 has not been added to the set
mySet.has(5);              // true
mySet.has(Math.sqrt(25));  // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true

mySet.size; // 4

mySet.delete(5); // removes 5 from the set
mySet.has(5);    // false, 5 has been removed

mySet.size; // 3, we just removed one value

2

Đây là một cách duy nhất để làm điều đó (mặc dù nó không duy trì thứ tự). Thêm vào đó, kết quả cũng sẽ được yêu cầu, rất hữu ích trong hầu hết các trường hợp:

<select ng-model="orderProp" >
   <option ng-repeat="place in places | orderBy:'category' as sortedPlaces" data-ng-if="sortedPlaces[$index-1].category != place.category" value="{{place.category}}">
      {{place.category}}
   </option>
</select>

2

Không có bộ lọc nào ở trên khắc phục được sự cố của tôi nên tôi phải sao chép bộ lọc từ tài liệu github chính thức . Và sau đó sử dụng nó như được giải thích trong các câu trả lời ở trên

angular.module('yourAppNameHere').filter('unique', function () {

Hàm trả về (mục, bộ lọcOn) {

if (filterOn === false) {
  return items;
}

if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) {
  var hashCheck = {}, newItems = [];

  var extractValueToCompare = function (item) {
    if (angular.isObject(item) && angular.isString(filterOn)) {
      return item[filterOn];
    } else {
      return item;
    }
  };

  angular.forEach(items, function (item) {
    var valueToCheck, isDuplicate = false;

    for (var i = 0; i < newItems.length; i++) {
      if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
        isDuplicate = true;
        break;
      }
    }
    if (!isDuplicate) {
      newItems.push(item);
    }

  });
  items = newItems;
}
return items;
  };

});

1

Có vẻ như mọi người đang ném phiên bản uniquebộ lọc của riêng họ vào vòng, vì vậy tôi cũng sẽ làm như vậy. Phê bình rất được hoan nghênh.

angular.module('myFilters', [])
  .filter('unique', function () {
    return function (items, attr) {
      var seen = {};
      return items.filter(function (item) {
        return (angular.isUndefined(attr) || !item.hasOwnProperty(attr))
          ? true
          : seen[item[attr]] = !seen[item[attr]];
      });
    };
  });

1

Nếu bạn muốn nhận dữ liệu duy nhất dựa trên khóa lồng nhau:

app.filter('unique', function() {
        return function(collection, primaryKey, secondaryKey) { //optional secondary key
          var output = [], 
              keys = [];

          angular.forEach(collection, function(item) {
                var key;
                secondaryKey === undefined ? key = item[primaryKey] : key = item[primaryKey][secondaryKey];

                if(keys.indexOf(key) === -1) {
                  keys.push(key);
                  output.push(item);
                }
          });

          return output;
        };
    });

Gọi nó như thế này:

<div ng-repeat="notify in notifications | unique: 'firstlevel':'secondlevel'">

0

Thêm bộ lọc này:

app.filter('unique', function () {
return function ( collection, keyname) {
var output = [],
    keys = []
    found = [];

if (!keyname) {

    angular.forEach(collection, function (row) {
        var is_found = false;
        angular.forEach(found, function (foundRow) {

            if (foundRow == row) {
                is_found = true;                            
            }
        });

        if (is_found) { return; }
        found.push(row);
        output.push(row);

    });
}
else {

    angular.forEach(collection, function (row) {
        var item = row[keyname];
        if (item === null || item === undefined) return;
        if (keys.indexOf(item) === -1) {
            keys.push(item);
            output.push(row);
        }
    });
}

return output;
};
});

Cập nhật đánh dấu của bạn:

<select ng-model="orderProp" >
   <option ng-repeat="place in places | unique" value="{{place.category}}">{{place.category}}</option>
</select>

0

Điều này có thể là quá mức cần thiết, nhưng nó làm việc cho tôi.

Array.prototype.contains = function (item, prop) {
var arr = this.valueOf();
if (prop == undefined || prop == null) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == item) {
            return true;
        }
    }
}
else {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i][prop] == item) return true;
    }
}
return false;
}

Array.prototype.distinct = function (prop) {
   var arr = this.valueOf();
   var ret = [];
   for (var i = 0; i < arr.length; i++) {
       if (!ret.contains(arr[i][prop], prop)) {
           ret.push(arr[i]);
       }
   }
   arr = [];
   arr = ret;
   return arr;
}

Hàm riêng biệt phụ thuộc vào hàm chứa được xác định ở trên. Nó có thể được gọi là array.distinct(prop);nơi prop là tài sản bạn muốn khác biệt.

Vì vậy, bạn chỉ có thể nói $scope.places.distinct("category");


0

Tạo mảng của riêng bạn.

<select name="cmpPro" ng-model="test3.Product" ng-options="q for q in productArray track by q">
    <option value="" >Plans</option>
</select>

 productArray =[];
angular.forEach($scope.leadDetail, function(value,key){
    var index = $scope.productArray.indexOf(value.Product);
    if(index === -1)
    {
        $scope.productArray.push(value.Product);
    }
});
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.