angularjs - array - ng repeat distinct values
Cómo hacer que ng-repeat filtre los resultados duplicados (16)
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
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:
<select ng-model="orderProp" >
<option ng-repeat="place in places" value="{{place.category}}">{{place.category}}</option>
</select>
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
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:
<select ng-model="orderProp" >
<option ng-repeat="place in places | unique" value="{{place.category}}">{{place.category}}</option>
</select>
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:
<div ng-repeat = "item in items | unique:''name''">
{{ item.name }}
</div>
<script src="your/filters.js"></script>
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:
<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>
Crea tu propia matriz.
<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);
}
});
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<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;
};
});
Ejemplo
<div ng-repeat="item in items | unique : ''subitem.subitem.subitem.value''"></div>
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;
};
});
O puede escribir su propio filtro usando lodash.
app.filter(''unique'', function() {
return function (arr, field) {
return _.uniq(arr, function(a) { return a[field]; });
};
});
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]];
});
};
});
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
<th>Customer list: </th>
<tr ng-repeat="order in orders | unique: ''customer.id''" >
<td> {{ order.customer.name }} , {{ order.customer.id }} </td>
</tr>
resultado
Lista de clientes:
foo 10
bar 20
baz 30
Puede usar el filtro exclusivo de AngularUI (código fuente disponible aquí: filtro único AngularUI ) y usarlo directamente en ng-options (o 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>
Si desea enumerar categorías, creo que debe indicar explícitamente su intención en la vista.
<select ng-model="orderProp" >
<option ng-repeat="category in categories"
value="{{category}}">
{{category}}
</option>
</select>
en el controlador:
$scope.categories = $scope.places.reduce(function(sum, place) {
if (sum.indexOf( place.category ) < 0) sum.push( place.category );
return sum;
}, []);
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í:
<div ng-repeat="notify in notifications | unique: ''firstlevel'':''secondlevel''">
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();
}
};
}
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<l;i+=1) {
o[arr[i][field]] = arr[i];
}
for(i in o) {
r.push(o[i]);
}
return r;
};
})
y entonces
var colors=$filter(''unique'')(items,"color");