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

JVG
sumber
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>
jpmorin
sumber
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]; });
    };
});
Mike Ward
sumber
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

a8m
sumber
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");
Eduardo Ortiz
sumber
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;
}, []);
Omong kosong
sumber
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>
Erik Trautman
sumber
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>
kennasoft
sumber
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(); 
    }

  };
}
Helzgate
sumber
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
CommonSenseCode
sumber
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>
sepatu
sumber
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;
  };

});
Mamba hitam
sumber
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]];
      });
    };
  });
LS
sumber
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'">
thethakuri
sumber
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>
Shawn Dotey
sumber
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");

Ikechi Michael
sumber
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);
    }
});
Akhilesh Kumar
sumber