如何使ng-repeat过滤掉重复的结果

我正在运行一个简单的ng-repeat一个JSON文件,并希望得到类别名称。 大约有100个物体,每个物体属于一个类别 – 但只有大约6个类别。

我目前的代码是这样的:

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

输出是100个不同的选项,大部分是重复的。 如何使用Angular检查{{place.category}}是否已经存在,如果已经存在,不要创build一个选项?

编辑:在我的javascript, $scope.places = JSON data ,只是为了澄清

您可以使用AngularUI中独特的filter(源代码在这里: AngularUI独特的filter ),并直接在ng-options(或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> 

或者你可以使用lodash写你自己的filter。

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

您可以在angular.filter模块中使用“unique”(别名:uniq)filter

用法: colection | uniq: 'property' colection | uniq: 'property'
你也可以通过嵌套属性进行过滤: colection | uniq: 'property.nested_property' colection | uniq: 'property.nested_property'

你能做什么就是这样

 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:我们按客户ID进行过滤,即删除重复的客户

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

结果
客户名单:
富10
酒吧20
巴斯30

这段代码适用于我。

 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; }; }) 

接着

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

如果你想列出类别,我认为你应该在视图中明确说明你的意图。

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

在控制器中:

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

UPDATE

我推荐使用Set,但是对于ng-repeat,这不起作用,也不能使用Map,因为ng-repeat只能用于数组。 所以忽略这个答案。 反正如果你需要过滤出重复的方式是像其他人所说的使用angular filters ,这里是它的入门部分的链接 。


老答案

哟可以使用ECMAScript 2015(ES6)标准设置数据结构 ,而不是一个arrays数据结构这种方式过滤重复的值,当添加到设置。 (记住集合不允许重复的值)。 真的好用:

 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 

这是一个简单而通用的例子。

filter:

 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; }; }) 

标记:

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

我决定延长@ thethakuri的答案,允许任何深度的唯一成员。 这是代码。 这对于那些不希望包含整个AngularUI模块的人来说只是为了这个function。 如果您已经使用AngularUI,请忽略此答案:

 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; }; }); 

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

我有一个string数组,而不是对象,我用这种方法:

 ng-repeat="name in names | unique" 

有了这个filter:

 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(); } }; } 

似乎每个人都把自己的uniquefilter版本扔到环,所以我会做同样的。 批评是非常受欢迎的。

 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]]; }); }; }); 

如果你想获得基于嵌套键的唯一数据:

 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; }; }); 

像这样调用它:

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

创build你自己的数组。

 <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); } }); 

这是一个模板唯一的方法来做到这一点(它不是维持秩序,但)。 另外,结果也会被sorting,这在大多数情况下很有用:

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

添加这个filter:

 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; }; }); 

更新你的标记:

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

这可能是矫枉过正,但它适用于我。

 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; } 

独特的function取决于上面定义的包含function。 它可以被称为array.distinct(prop); prop是你想要区分的财产。

所以你可以只说$scope.places.distinct("category");

以上都没有解决我的问题,所以我不得不从官方github文档复制filter。 然后按照上面的答案解释

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

返回函数(items,filterOn){

 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; }; });