Cómo hacer que ng-repeat filtre los resultados duplicados

Estoy ejecutando un simple ng-repeat sobre un archivo JSON y quiero obtener nombres de categoría. Hay alrededor de 100 objetos, cada uno perteneciente a una categoría, pero solo hay alrededor de 6 categorías.

Mi código actual es este:

  {{place.category}}  

El resultado es 100 opciones diferentes, en su mayoría duplicados. ¿Cómo uso Angular para verificar si ya existe {{place.category}} y no creo una opción si ya está allí?

editar: en mi javascript, $scope.places = JSON data , solo para aclarar

Puede usar el filtro exclusivo de AngularUI (código fuente disponible aquí: filtro único AngularUI ) y usarlo directamente en ng-options (o ng-repeat).

  

O puede escribir su propio filtro usando lodash.

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

Puede usar el filtro ‘unique’ (aliases: uniq) en el módulo angular.filter

uso: colection | uniq: 'property' colection | uniq: 'property'
también puede filtrar por propiedades anidadas: colection | uniq: 'property.nested_property' colection | uniq: 'property.nested_property'

Lo que puedes hacer es algo así …

 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: Filtramos por ID de cliente, es decir, eliminamos clientes duplicados

 Customer list:    {{ order.customer.name }} , {{ order.customer.id }}   

resultado
Lista de clientes:
foo 10
bar 20
baz 30

este código funciona para mí.

 app.filter('unique', function() { return function (arr, field) { var o = {}, i, l = arr.length, r = []; for(i=0; i 

y entonces

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

Si desea enumerar categorías, creo que debe indicar explícitamente su intención en la vista.

  

en el controlador:

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

Aquí hay un ejemplo directo y genérico.

El filtro:

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

El marcado:

 
{{ item.name }}

ACTUALIZAR

Estaba recomendando el uso de Set, pero siento que esto no funciona para ng-repeat, ni Map, ya que ng-repeat solo funciona con array. Entonces ignora esta respuesta. de todos modos si necesita filtrar los duplicados de una manera es como otros han dicho usando angular filters , aquí está el enlace para la sección de inicio .


Respuesta anterior

Puede utilizar la estructura de conjunto de datos estándar de ECMAScript 2015 (ES6) , en lugar de una estructura de datos de matriz, de esta forma puede filtrar valores repetidos al agregarlos al conjunto. (Recuerde que los juegos no permiten valores repetidos). Realmente fácil de usar:

 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 

Decidí extender la respuesta de @ thethakuri para permitir cualquier profundidad para el miembro único. Aquí está el código. Esto es para aquellos que no desean incluir todo el módulo AngularUI solo para esta funcionalidad. Si ya está usando AngularUI, ignore esta respuesta:

 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 

Ejemplo

 

Tenía una serie de cadenas, no objetos, y utilicé este enfoque:

 ng-repeat="name in names | unique" 

con este filtro:

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

Parece que todos están lanzando su propia versión del filtro unique en el ring, así que haré lo mismo. Crítica es muy bienvenida.

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

Si desea obtener datos únicos basados ​​en la clave anidada:

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

Llámalo así:

 

Crea tu propia matriz.

  productArray =[]; angular.forEach($scope.leadDetail, function(value,key){ var index = $scope.productArray.indexOf(value.Product); if(index === -1) { $scope.productArray.push(value.Product); } }); 

Aquí hay una forma de solo plantilla para hacerlo (sin embargo, no es mantener el orden). Además, el resultado será ordenado también, lo cual es útil en la mayoría de los casos:

  

Agregar este filtro:

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

Actualiza tu marcado:

  

Esto podría ser excesivo, pero funciona para mí.

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

La función distinta depende de la función contiene definida anteriormente. Se puede llamar como array.distinct(prop); donde prop es la propiedad que desea que sea distinta.

Entonces podría decir $scope.places.distinct("category");

Ninguno de los filtros anteriores solucionó mi problema, así que tuve que copiar el filtro del documento oficial de github. Y luego usarlo como se explica en las respuestas anteriores

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

función de retorno (elementos, 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; }; });