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


131

我正在运行一个简单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}}已经存在,如果已经存在则不创建选项?

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


1
您为什么不只删除$ scope.places?使用jquery映射api.jquery.com/map
anazimok

您最终的工作解决方案是什么?是在上面还是在进行
重复数据

我想知道解决方案。请发布后续消息。谢谢!
jdstein1 2015年

@ jdstein1我将从TLDR开始:使用以下答案,或使用香草Javascript仅过滤出数组中的唯一值。我做了什么:最后,这是我的逻辑和对MVC的理解的问题。我从MongoDB加载数据,要求转储数据,并希望Angular神奇地将其过滤到唯一的位置。解决方案(通常是这样)是避免变得懒惰并修复我的数据库模型-对我来说,它被称为Mongo's db.collection.distinct("places"),这比在Angular中进行改进要好得多!遗憾的是,这并不适合所有人。
JVG

感谢更新!
jdstein1 2015年

Answers:


142

您可以使用AngularUI中的唯一过滤器(此处提供源代码:AngularUI唯一过滤器),然后直接在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>

33
对于那些无法使用AngularUI的唯一过滤器的用户:过滤器位于单独的模块中:例如,您必须将其作为附加引用包含在模块中angular.module('yourModule', ['ui', 'ui.filters']);。直到我查看AngularUI js文件内部之前,都感到很困惑。
GFoley83

8
unique当前可以在AngularJs UI Utils中
Nick

2
在新版本的ui utils中,您可以仅包含ui.unique。仅对模块使用Bower安装。
以示例方式

如果您不想包括AngularUI及其全部内容,但想使用唯一的过滤器,则只需将unique.js源粘贴复制到您的应用中,然后更改angular.module('ui.filters')为您的应用名称。
chakeda '16

37

或者,您可以使用lodash编写自己的过滤器。

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

您好迈克,看起来非常优雅,但是当我通过诸如unique:status [我的字段名称]之类的过滤器时,它似乎没有按预期工作,它仅返回第一个结果,而不是所需的所有可用唯一雕像的列表。任何猜测为什么?
SinSync 2014年

3
尽管我使用了下划线,但此解决方案还是很有魅力。谢谢!
乔恩·布莱克

非常优雅的解决方案。
JD史密斯

1
lodash是下划线的分支。它具有更好的性能和更多的实用程序。
Mike Ward

2
lodash v4中_.uniqBy(arr, field);应该适用于嵌套属性
ijavid

30

您可以在angular.filter模块中 使用'unique'(别名:uniq)过滤器

用法:colection | uniq: '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>

结果
客户清单:
foo 10
bar 20
baz 30


喜欢您的答案,但很难将其转化为我的问题。您将如何处理嵌套列表。例如,一个订单列表包含每个订单的项目列表。在您的示例中,每个订单有一个客户。我希望看到所有订单中的商品的唯一列表。不要太在意这一点,但您也可以将其视为客户有很多订单,而订单有很多项目。如何显示客户已订购的所有先前物品?有什么想法吗?
格雷格·格拉特

@GregGrater可以提供与您的问题类似的示例对象吗?
2014年

谢谢@Ariel M.!以下是数据示例:
Greg Grater 2014年

它与哪些版本的angular兼容?
Icet

15

该代码对我有用。

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

2
l的定义应为l = arr!= undefined?arr.length:0,因为否则在angularjs中会出现解析错误
Gerrit

6

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

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

您能再解释一下吗?我想连续为每个唯一类别重复元素。JS是否只进入定义了控制器的JS文件中?
mark1234 2014年

如果要对选项进行分组,则是另一个问题。您可能需要研究optgroup元素。Angular支持optgroup。group byselect指令中搜索表达式。
Tosh 2014年

在添加/删除位置的情况下会发生吗?如果相关类别是场所中的唯一实例,是否将添加/删除相关类别?
格雷格·格拉特

4

这是一个简单直接的示例。

过滤器:

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>

这工作得很好,但它在控制台抛出一个错误Cannot read property 'length' of undefined在该行l = arr.length
灵魂Eeater

4

我决定扩展@thethakuri的答案,以允许唯一成员有任何深度。这是代码。这是针对那些不想仅为此功能包括整个AngularUI模块的用户的。如果您已经在使用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>

2

我有一个字符串数组,而不是对象,并且我使用了这种方法:

ng-repeat="name in names | unique"

使用此过滤器:

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

更新

我建议使用Set,但是很抱歉,这不适用于ng-repeat或Map,因为ng-repeat仅适用于数组。因此,请忽略此答案。无论如何,如果您需要一种过滤重复的方法,如其他人所说的angular filters,这里是入门章节链接


旧答案

您可以使用ECMAScript 2015(ES6)标准Set Data结构,而不是Array Data Structure,这样您可以在添加到Set中时过滤重复的值。(记住集合不允许重复的值)。确实好用:

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

这是一种仅用于模板的方法(不过,它并不维护顺序)。另外,结果也将被排序,这在大多数情况下很有用:

<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

上述过滤器均未解决我的问题,因此我不得不从官方github doc复制过滤器 然后按照上述答案中的说明使用它

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

返回函数(item,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

似乎每个人都将自己的unique过滤器版本扔进了戒指,所以我会做同样的事情。批评是非常欢迎的。

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

如果要基于嵌套键获取唯一数据:

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

0

添加此过滤器:

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>

0

这可能有点过分,但是对我有用。

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

独特的功能取决于上面定义的包含功能。可以将其称为array.distinct(prop);prop是要与众不同的属性。

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


0

创建自己的数组。

<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);
    }
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.