web-dev-qa-db-fra.com

Comment faire pour que ng-repeat filtre les résultats en double

J'exécute un simple ng-repeat sur un fichier JSON et je veux obtenir les noms de catégorie. Il y a environ 100 objets, chacun appartenant à une catégorie - mais il n'y a qu'environ 6 catégories. 

Mon code actuel est le suivant:

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

La sortie contient 100 options différentes, principalement des doublons. Comment utiliser Angular pour vérifier si un {{place.category}} existe déjà et ne pas créer une option s'il y en a déjà une?

edit: Dans mon javascript, $scope.places = JSON data, juste pour clarifier

124
JVG

Vous pouvez utiliser le filtre unique de AngularUI (code source disponible ici: AngularUI unique filter ) et l’utiliser directement dans ng-options (ou 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>
137
jpmorin

Ou vous pouvez écrire votre propre filtre en utilisant lodash.

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

Vous pouvez utiliser le filtre 'unique' (aliases: uniq) dans angular.filter module

utilisation: colection | uniq: 'property' 
vous pouvez également filtrer par propriétés imbriquées: colection | uniq: 'property.nested_property'

Qu'est-ce que vous pouvez faire, c'est quelque chose comme ça ..

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: Nous filtrons par ID client, c'est-à-dire supprimons les clients en double.

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

résultat
Liste de clients:
foo 10
bar 20
baz 30

28
a8m

ce code fonctionne pour moi.

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

et alors

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

Si vous souhaitez lister les catégories, je pense que vous devriez indiquer explicitement votre intention Dans la vue.

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

dans le contrôleur:

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

J'ai décidé d'étendre la réponse de @ thethakuri pour permettre une profondeur au membre unique. Voici le code. Ceci est destiné à ceux qui ne souhaitent pas inclure le module AngularUI complet uniquement pour cette fonctionnalité. Si vous utilisez déjà AngularUI, ignorez cette réponse:

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

Exemple

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

Voici un exemple simple et générique.

Le filtre:

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

Le balisage:

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

METTRE À JOUR

Je recommandais l'utilisation de Set mais je regrette que cela ne fonctionne pas pour ng-repeat, ni Map car ng-repeat ne fonctionne qu'avec array. Alors ignorez cette réponse. Quoi qu'il en soit, si vous avez besoin de filtrer les doublons d'une manière, comme d'autres l'ont déjà dit, utilisez angular filters, voici le lien correspondant à la section pour commencer .


Ancienne réponse

Vous pouvez également utiliser la structure de données Set standard - ECMAScript 2015 (ES6) , au lieu d’une structure de données de tableau de cette façon, vous filtrez les valeurs répétées lors de l’ajout au jeu. (Rappelez-vous que les ensembles n'autorisent pas les valeurs répétées). Vraiment facile à utiliser:

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
CommonSenseCode

J'ai eu un tableau de chaînes, pas d'objets et j'ai utilisé cette approche:

ng-repeat="name in names | unique"

avec ce filtre:

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

  };
}
1
Helzgate

Si vous souhaitez obtenir des données uniques basées sur la clé imbriquée:

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

Appelez ça comme ça:

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

Il semble que tout le monde jette sa propre version du filtre unique dans l'anneau, je vais donc en faire autant La critique est la bienvenue.

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

Voici un moyen de le faire uniquement sur un modèle (il ne s'agit toutefois pas de maintenir l'ordre). De plus, le résultat sera commandé, ce qui est utile dans la plupart des cas:

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

Aucun des filtres ci-dessus n'a résolu mon problème et j'ai donc dû copier le filtre à partir du document officiel github. Et ensuite utilisez-le comme expliqué dans les réponses ci-dessus

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

fonction de retour (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;
  };

});
1
Black Mamba

Ajouter ce filtre:

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

Mettez à jour votre balisage:

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

C'est peut-être exagéré, mais cela fonctionne pour moi.

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 fonction distincte dépend de la fonction contient définie ci-dessus. On peut l'appeler array.distinct(prop); où prop est la propriété que vous voulez différencier.

Donc, vous pouvez simplement dire $scope.places.distinct("category"); 

0
Ikechi Michael

Créez votre propre tableau.

<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);
    }
});
0
Akhilesh Kumar