ng-repeat에서 중복 결과를 필터링하는 방법


131

ng-repeatJSON 파일을 단순하게 실행하고 있으며 카테고리 이름을 가져오고 싶습니다. 각각 하나의 범주에 속하는 약 100 개의 개체가 있지만 약 6 개의 범주 만 있습니다.

내 현재 코드는 다음과 같습니다

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

출력은 100 가지 옵션으로, 대부분 복제됩니다. Angular를 사용하여 {{place.category}}이미 존재 하는지 확인하고 이미 존재하는 경우 옵션을 작성하지 않으려면 어떻게합니까?

편집 : 내 자바 스크립트에서 $scope.places = JSON data명확히하기 위해


1
왜 $ scope.places를 중복 제거하지 않습니까? jquery 맵 사용 api.jquery.com/map
anazimok

최종 작업 솔루션은 무엇입니까? 위에 있거나 중복 제거를 수행하는 JS가
있습니까?

이에 대한 해결책을 알고 싶습니다. 후속 조치를 게시하십시오. 감사!
jdstein1

@ jdstein1 TLDR로 시작하겠습니다. 아래 답변을 사용하거나 vanilla Javascript를 사용하여 배열의 고유 값만 필터링하십시오. 내가 한 일 : 결국, 그것은 내 논리와 MVC에 대한 이해에 문제가되었습니다. MongoDB에서 데이터 덤프를 요청하는 데이터를로드하고 Angular가 마술처럼 독특한 장소로 데이터를 필터링하기를 원했습니다. 해결책은 종종 경우처럼 게으 르기를 멈추고 DB 모델을 수정하는 것이 었습니다 .Mongo 's라고 부르는 db.collection.distinct("places")것이 Angular 내에서보다 훨씬 좋았습니다! 슬프게도 이것은 모든 사람에게 효과적이지 않습니다.
JVG

업데이트 해 주셔서 감사합니다!
jdstein1

답변:


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

2
ui utils의 새 버전에서는 ui.unique 만 포함 할 수 있습니다. 모듈에만 바우어 설치를 사용하십시오.
예 :

AngularUI와 전체를 포함하고 싶지는 않지만 고유 한 필터를 사용하려면 unique.js 소스를 앱에 붙여 넣은 다음 angular.module('ui.filters')앱 이름으로 변경 하면됩니다.
chakeda

37

또는 lodash를 사용하여 고유 한 필터를 작성할 수 있습니다.

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

Hello Mike, 정말 우아해 보이지만 unique : status [my field name]와 같은 필터를 전달할 때 예상대로 작동하지 않는 것 같습니다. 사용 가능한 모든 고유 조각상의 원하는 목록과 달리 첫 번째 결과 만 반환합니다. 왜 그런지 알아?
SinSync

3
밑줄을 사용했지만이 솔루션은 매력처럼 작동했습니다. 감사!
Jon Black

매우 우아한 솔루션.
JD Smith

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


당신의 대답처럼, 그러나 그것을 내 문제로 번역하는 데 어려움을 겪고 있습니다. 중첩 된 목록을 어떻게 처리 하시겠습니까? 예를 들어, 각 주문에 대한 품목 목록이 포함 된 주문 목록입니다. 예를 들어, 주문 당 하나의 고객이 있습니다. 모든 주문에서 고유 한 항목 목록을보고 싶습니다. 요점을 설명하지 말고 고객이 많은 주문을 가지고 있고 주문에 많은 품목이 있다고 생각할 수도 있습니다. 고객이 주문한 이전 항목을 모두 표시하려면 어떻게해야합니까? 생각?
Greg Grater

@GregGrater 문제와 유사한 예제 개체를 제공 할 수 있습니까?
a8m

감사합니다 @Ariel M.! 데이터의 예는 다음과 같습니다.
Greg Grater

어떤 버전의 각도와 호환됩니까?
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

옵션을 그룹화하려면 다른 질문입니다. optgroup 요소를 살펴볼 수 있습니다. 각도는 optgroup을 지원합니다. 지시문 에서 group by표현식을 검색하십시오 select.
Tosh 2016 년

장소가 추가 / 삭제되는 경우에 발생합니까? 연관된 카테고리가 유일한 인스턴스 인 경우 추가 / 제거됩니까?
Greg

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
Soul 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에서는 배열이 작동하지 않으므로 ng-repeat에서는 작동하지 않으며 Map도 작동하지 않습니다. 따라서이 답변을 무시하십시오. 어쨌든, 다른 방법으로 사용하는 것처럼 중복을 필터링 해야하는 경우 시작 섹션에 대한 링크angular filters 는 다음 과 같습니다 .


이전 답변

세트에 추가 할 때 반복되는 값을 필터링하는 방식으로 배열 데이터 구조 대신 ECMAScript 2015 (ES6) 표준 세트 데이터 구조를 사용할 수 있습니다 . (세트는 반복되는 값을 허용하지 않습니다.) 정말 사용하기 쉬운 :

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 () {

리턴 함수 (항목, 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);
    }
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.