Cara membuat ng-repeat filter out hasil duplikat


131

Saya menjalankan sederhana di ng-repeatatas file JSON dan ingin mendapatkan nama kategori. Ada sekitar 100 objek, masing-masing milik kategori - tetapi hanya ada sekitar 6 kategori.

Kode saya saat ini adalah:

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

Outputnya adalah 100 opsi berbeda, kebanyakan duplikat. Bagaimana cara menggunakan Angular untuk memeriksa apakah {{place.category}}sudah ada, dan tidak membuat opsi jika sudah ada?

sunting: Di javascript saya $scope.places = JSON data,, hanya untuk memperjelas


1
Mengapa Anda tidak memotong $ scope.places saja? gunakan peta jquery api.jquery.com/map
anazimok

apa solusi terakhir Anda? IS itu di atas atau ada beberapa JS melakukan de-
duping

Saya ingin tahu solusinya. Silakan posting tindak lanjut. Terima kasih!
jdstein1

@ jdstein1 Saya akan mulai dengan TLDR: Gunakan jawaban di bawah ini, atau gunakan Javascript vanilla untuk menyaring hanya nilai-nilai unik dalam array. Apa yang saya lakukan: Pada akhirnya, itu adalah masalah dengan logika dan pemahaman saya tentang MVC. Saya sedang memuat data dari MongoDB meminta dump data dan ingin Angular menyaringnya secara ajaib ke tempat yang unik saja. Solusinya, seperti yang sering terjadi, adalah berhenti menjadi malas dan memperbaiki model DB saya - bagi saya, itu disebut Mongo db.collection.distinct("places"), yang jauh, jauh lebih baik daripada melakukannya di dalam Angular! Sayangnya ini tidak akan bekerja untuk semua orang.
JVG

terima kasih atas pembaruannya!
jdstein1

Jawaban:


142

Anda dapat menggunakan filter unik dari AngularUI (kode sumber tersedia di sini: filter unik AngularUI ) dan menggunakannya langsung di opsi-ng (atau 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
Bagi mereka yang tidak bisa mendapatkan filter unik dari AngularUI berfungsi: filter berada dalam modul terpisah: Misalnya Anda harus memasukkannya sebagai referensi tambahan dalam modul Anda angular.module('yourModule', ['ui', 'ui.filters']);. Bingung sampai saya melihat di dalam file js AngularUI.
GFoley83

8
The uniqueFilter saat ini dapat ditemukan sebagai bagian dari AngularJs UI Utils
Nick

2
Di versi baru ui utils, Anda bisa memasukkan ui.unique sendiri. gunakan install bower hanya untuk modul.
Belajar statistik dengan contoh

Jika Anda tidak ingin memasukkan AngularUI dan keseluruhannya, tetapi ingin menggunakan filter yang unik, Anda bisa menyalin paste sumber unique.js ke dalam aplikasi Anda, kemudian ubah angular.module('ui.filters')ke nama aplikasi Anda.
chakeda

37

Atau Anda dapat menulis filter sendiri menggunakan lodash.

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

Halo Mike, terlihat sangat elegan tetapi tampaknya tidak berfungsi seperti yang diharapkan ketika saya melewati filter seperti unik: status [nama bidang saya] itu hanya mengembalikan hasil pertama sebagai lawan dari daftar yang diinginkan dari semua patung unik yang tersedia. Ada tebakan mengapa?
SinSync

3
Meskipun saya menggunakan garis bawah, solusi ini bekerja seperti pesona. Terima kasih!
Jon Black

Solusi yang sangat elegan.
JD Smith

1
lodash adalah garpu garis bawah. Ini memiliki kinerja yang lebih baik dan lebih banyak utilitas.
Mike Ward

2
di lodash v4 _.uniqBy(arr, field);harus bekerja untuk properti bersarang
ijavid

30

Anda dapat menggunakan filter 'unik' (alias: uniq) dalam modul angular.filter

penggunaan: colection | uniq: 'property'
Anda juga dapat memfilter menurut properti bersarang: colection | uniq: 'property.nested_property'

Apa yang dapat Anda lakukan, adalah sesuatu seperti itu ..

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: Kami memfilter menurut id pelanggan, yaitu menghapus pelanggan duplikat

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


daftar hasil Pelanggan:
foo 10
bar 20
baz 30


suka jawaban Anda, tetapi kesulitan menerjemahkannya ke masalah saya. Bagaimana Anda menangani daftar bersarang. Misalnya, daftar pesanan yang berisi daftar barang untuk setiap pesanan. Dalam contoh Anda, Anda memiliki satu pelanggan per pesanan. Saya ingin melihat daftar barang unik di semua pesanan. Bukan untuk mempermasalahkan intinya, tetapi Anda juga dapat menganggapnya sebagai pelanggan memiliki banyak pesanan dan pesanan memiliki banyak item. Bagaimana saya bisa menunjukkan semua barang sebelumnya yang dipesan pelanggan? Pikiran?
Greg Grater

@GregGrater Bisakah Anda memberikan contoh objek yang mirip dengan masalah Anda?
a8m

Terima kasih @riel M.! Berikut adalah contoh datanya:
Greg Grater

Dengan versi sudut mana itu kompatibel?
Icet

15

kode ini berfungsi untuk saya.

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

lalu

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

2
Definisi l harus l = arr! = Tidak terdefinisi? arr.length: 0 karena jika tidak ada kesalahan parsing di angularjs
Gerrit

6

Jika Anda ingin mendaftar kategori, saya pikir Anda harus secara eksplisit menyatakan niat Anda dalam tampilan.

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

di controller:

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

bisakah Anda menjelaskan ini sedikit lebih banyak. Saya ingin mengulangi elemen-elemen berturut-turut untuk setiap kategori unik. Apakah JS hanya masuk dalam file JS di mana controller didefinisikan?
mark1234

Jika Anda ingin mengelompokkan opsi, itu adalah pertanyaan yang berbeda. Anda mungkin ingin melihat elemen optgroup. Angular mendukung optgroup. mencari group byekspresi dalam selectarahan.
Tosh

Apakah akan terjadi jika suatu tempat ditambahkan / dihapus? Apakah kategori yang terkait akan ditambahkan / dihapus jika itu adalah satu-satunya contoh di tempat?
Greg Grater

4

Berikut adalah contoh sederhana dan generik.

Filter:

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

Markup:

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

Ini berfungsi dengan baik tetapi melemparkan kesalahan di konsol Cannot read property 'length' of undefineddi barisl = arr.length
Soul Eeater

4

Saya memutuskan untuk memperluas jawaban @ thethakuri untuk memungkinkan kedalaman bagi anggota unik. Ini kodenya. Ini untuk mereka yang tidak ingin menyertakan seluruh modul AngularUI hanya untuk fungsi ini. Jika Anda sudah menggunakan AngularUI, abaikan jawaban ini:

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

Contoh

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

2

Saya memiliki serangkaian string, bukan objek dan saya menggunakan pendekatan ini:

ng-repeat="name in names | unique"

dengan filter ini:

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

MEMPERBARUI

Saya merekomendasikan penggunaan Set tetapi maaf ini tidak bekerja untuk ng-repeat, atau Map karena ng-repeat hanya bekerja dengan array. Jadi abaikan jawaban ini. Bagaimanapun jika Anda perlu memfilter duplikat satu cara adalah seperti yang dikatakan orang lain menggunakan angular filters, berikut adalah tautan untuk itu ke bagian memulai .


Jawaban lama

Anda dapat menggunakan struktur Set Data standar ECMAScript 2015 (ES6) , alih-alih Struktur Data Array dengan cara ini Anda memfilter nilai berulang ketika menambahkan ke Set. (Ingat set tidak memungkinkan nilai berulang). Sangat mudah digunakan:

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

Inilah cara khusus templat untuk melakukannya (meskipun tidak mempertahankan urutannya). Plus, hasilnya akan dipesan juga, yang berguna dalam kebanyakan kasus:

<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

Tidak ada filter di atas yang memperbaiki masalah saya jadi saya harus menyalin filter dari doc github resmi . Dan kemudian gunakan seperti yang dijelaskan dalam jawaban di atas

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

mengembalikan fungsi (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

Sepertinya semua orang melemparkan versi uniquefilter mereka sendiri ke dalam ring, jadi saya akan melakukan hal yang sama. Kritik sangat diterima.

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

Jika Anda ingin mendapatkan data unik berdasarkan kunci bersarang:

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

Sebut saja seperti ini:

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

0

Tambahkan filter ini:

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

Perbarui markup Anda:

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

0

Ini mungkin berlebihan, tetapi itu berhasil untuk saya.

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

Fungsi yang berbeda tergantung pada fungsi berisi yang didefinisikan di atas. Dapat disebut sebagai array.distinct(prop);prop adalah properti yang Anda ingin berbeda.

Jadi bisa dibilang begitu $scope.places.distinct("category");


0

Buat array Anda sendiri.

<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);
    }
});
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.