Bir nesne dizisinden, bir özelliğin değerini dizi olarak ayıklayın


1016

Aşağıdaki yapıya sahip JavaScript nesne dizisi var:

objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];

Her nesneden bir alan ayıklamak ve değerleri içeren bir dizi almak istiyorum, örneğin alan foodizi verecekti [ 1, 3, 5 ].

Bunu bu önemsiz yaklaşımla yapabilirim:

function getFields(input, field) {
    var output = [];
    for (var i=0; i < input.length ; ++i)
        output.push(input[i][field]);
    return output;
}

var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]

Özel bir yardımcı program işlevinin gereksiz olması için bunu yapmanın daha zarif veya deyimsel bir yolu var mı?


Önerilen yineleme hakkında not , tek bir nesnenin bir diziye nasıl dönüştürüleceğini kapsar .


4
Prototip kütüphanesi Array prototipine bir "pluck" fonksiyonu ekledi (bence), böylece yazabilirsiniz var foos = objArray.pluck("foo");.
Sivri

3
@hyde - jsperf.com/map-vs-native-for-loop - lütfen şuna bir göz atın, umarım sade kendini iyi bir çözüm haline getirin
N20084753

1
@ N20084753 adil bir test için, yerel Array.prototype.mapişlevi bulunduğu yerde de karşılaştırmalısınız
Alnitak

@Alnitak - ya haklısın ama yerel Array.prototype.map'in döngü için yerelden nasıl optimize edildiğini anlayamıyorum, herhangi bir şekilde tüm diziyi geçmemiz gerekiyor.
N20084753

3
OP, önerilen başkalarına yaklaşımınızı tercih ediyorum. Bunda yanlış bir şey yok.

Yanıtlar:


1334

İşte bunu başarmanın daha kısa bir yolu:

let result = objArray.map(a => a.foo);

VEYA

let result = objArray.map(({ foo }) => foo)

Ayrıca kontrol edebilirsiniz Array.prototype.map().


3
Peki, bu başka bir cevabın totymedli'nin yorumu ile aynı, ama hiçbiri diğer cevaplardan daha iyi (benim görüşüme göre) bir yol , yani ... Kabul edilen cevaba dönüştürmek.
hyde

4
@PauloRoberto Arrow işlevleri IE dışında her yerde desteklenir.
tgies

1
elbette, buna izin verildi, ancak IMHO, bu yanıtı objektif olarak daha iyi yapan hiçbir şey yok, ancak soruyu sorduğunuz sırada mevcut olmayan ve bazı tarayıcılarda desteklenmeyen bir sözdizimi kullanması dışında . Ayrıca, bu cevabın, bu cevabın gönderilmesinden yaklaşık bir yıl önce orijinal olarak kabul edilen cevapta yapılan yorumların doğrudan bir kopyası olduğunu da belirtmek isterim.
Alnitak

26
Benim bakış içinde, yeni işlevsellik kullanarak @Alnitak, olduğu nesnel daha iyi. Bu pasaj çok yaygındır, bu yüzden bunun intihal olduğuna ikna olmadım. Güncel olmayan cevapları en üstte tutmanın hiçbir değeri yoktur.
Rob

6
yorumlar cevap değildir, imo birisi cevap yerine yorum gönderirse, birisi cevabı kopyalarsa kendi hatasıdır.
Aequitas

618

Evet, ancak JavaScript'in ES5 özelliğine dayanır. Bu IE8 veya daha eski sürümlerde çalışmayacağı anlamına gelir.

var result = objArray.map(function(a) {return a.foo;});

ES6 uyumlu JS tercümanlarında kısalık için bir ok işlevi kullanabilirsiniz :

var result = objArray.map(a => a.foo);

Array.prototype.map belgeleri


5
Bu çözüm düzgün ve basit görünmektedir, ancak düz döngü yönteminden daha optimize edilmiştir.
N20084753

OP , sadece kodlanmış değil, herhangi bir alan elde etmek için bir yöntem istemiyor foomu?
Alnitak

2
ES6'da şunları yapabilirsinizvar result = objArray.map((a) => (a.foo));
Siyah

28
@Black Daha da iyisi:var result = objArray.map(a => a.foo);
totymedli

4
@FaizKhan Yıla dikkat edin. Bu yanıt 25 Ekim 2013 tarihinde yayınlanmıştır. "Kabul edilen" yanıt 11 Ekim 2017 tarihinde yayınlanmıştır. Herhangi bir şey varsa onay işaretinin diğerine gitmesi dikkat çekicidir.
Niet the Dark Absol

52

Lodash_.pluck() işlevini veya Alt _.pluck()çizgi işlevini kontrol edin . Her ikisi de tek bir işlev çağrısında tam olarak istediğinizi yapar!

var result = _.pluck(objArray, 'foo');

Güncelleme: _.pluck()Lodash v4.0.0 itibariyle kaldırıldı lehine, _.map()benzer bir şey ile birlikte Niet cevabı . _.pluck()hala Alt Çizgi'de kullanılabilir .

2 Güncelleme: As Mark işaret yorumlarda , Lodash v4 ve 4.3 arasında bir yere, yeni bir fonksiyon tekrar bu işlevselliği sağlayan eklendi. _.property()bir nesnede bir özelliğin değerini almak için bir işlev döndüren bir steno işlevidir.

Ayrıca, _.map()şimdi bir dizginin ikinci parametre olarak geçirilmesine izin verir ve bu da geçirilir _.property(). Sonuç olarak, aşağıdaki iki satır Lodash 4 öncesi yukarıdaki kod örneğine eşdeğerdir.

var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));

_.property()ve dolayısıyla _.map()alt özelliklere erişmek için nokta ile ayrılmış bir dize veya dizi sağlamanıza da olanak tanır:

var objArray = [
    {
        someProperty: { aNumber: 5 }
    },
    {
        someProperty: { aNumber: 2 }
    },
    {
        someProperty: { aNumber: 9 }
    }
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));

_.map()Yukarıdaki örnekteki her iki çağrı da geri dönecektir [5, 2, 9].

İşlevsel programlamaya biraz daha giriyorsanız , Ramda'nınR.pluck() işlevine bir göz atın ;

var result = R.pluck('foo')(objArray);  // or just R.pluck('foo', objArray)

5
İyi haber: Lodash 4.0.0 ve 4.3.0 _.property('foo')( lodash.com/docs#property ) arasında bir kısayol olarak eklendi function(o) { return o.foo; }. Bu, Lodash kullanım durumunu kısaltır var result = _.pluck(objArray, _.property('foo'));Daha fazla kolaylık sağlamak için, Lodash 4.3.0'ın _.map() yöntemi de _.property()kaputun altında bir stenografi yapılmasına izin verir , sonuçtavar result = _.map(objArray, 'foo');
Mark A. Fitzgerald

45

Sadece JS çözümleri için konuşurken, basit bir endeksli fordöngünün alternatiflerinden daha performanslı olduğunu gördüm .

100000 öğe dizisinden tek özellik ayıklanıyor (jsPerf aracılığıyla)

Döngü 368 Ops / sn için geleneksel

var vals=[];
for(var i=0;i<testArray.length;i++){
   vals.push(testArray[i].val);
}

Döngü için ES6 303 Ops / sn

var vals=[];
for(var item of testArray){
   vals.push(item.val); 
}

Dizi.prototip.map 19 Ops / sn

var vals = testArray.map(function(a) {return a.val;});

TL; DR - .map () yavaş, ancak okunabilirliğin performanstan daha değerli olduğunu düşünüyorsanız bunu kullanmaktan çekinmeyin.

Düzenleme # 2: 6/2019 - jsPerf bağlantısı koptu, kaldırıldı.


1
Bana ne istediğini söyle, ama performans önemli. Haritayı anlayabiliyorsanız bir for döngüsünü anlayabilirsiniz.
illcrx

Kıyaslama sonuçlarınızı öğrenmek yararlı olsa da, bu soruyu cevaplamadığı için burada uygun olmadığını düşünüyorum. Bunu geliştirmek için ya gerçek bir cevap ekleyin ya da başka bir yorumda sıkıştırın (mümkünse).
Ifedi Okonkwo

15

Kullanma Array.prototype.map:

function getFields(input, field) {
    return input.map(function(o) {
        return o[field];
    });
}

ES5 öncesi tarayıcılar için bir şim için yukarıdaki bağlantıya bakın.


15

Çapraz tarayıcı güvencesi için lodash veya alt çizgi gibi bir tür kitaplık kullanmak daha iyidir.

Lodash'da aşağıdaki yöntemi kullanarak dizideki bir özelliğin değerlerini alabilirsiniz

_.map(objArray,"foo")

ve Alt Çizgi'de

_.pluck(objArray,"foo")

Her ikisi de geri dönecek

[1, 2, 3]

10

ES6'da şunları yapabilirsiniz:

const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)

Foo undefined ise ne olacak? Tanımsız bir dizi iyi değil.
godhar

6

mapNesneler listesinden 'sütunları' seçmek için uygun bir çözüm olsa da, bir dezavantajı vardır. Sütunların olup olmadığını açıkça kontrol etmezseniz, bir hata atar ve (en iyi ihtimalle) size sunar undefined. Sadece reduceözelliği görmezden gelebilir veya hatta varsayılan bir değerle ayarlayabileceğiniz bir çözüm seçerim.

function getFields(list, field) {
    //  reduce the provided list to an array only containing the requested field
    return list.reduce(function(carry, item) {
        //  check if the item is actually an object and does contain the field
        if (typeof item === 'object' && field in item) {
            carry.push(item[field]);
        }

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbin örneği

Bu, sağlanan listedeki öğelerden biri bir nesne olmasa veya alanı içermese bile işe yarar.

Bir öğe nesne değilse veya alanı içermiyorsa, varsayılan bir değer üzerinde anlaşarak daha esnek hale getirilebilir.

function getFields(list, field, otherwise) {
    //  reduce the provided list to an array containing either the requested field or the alternative value
    return list.reduce(function(carry, item) {
        //  If item is an object and contains the field, add its value and the value of otherwise if not
        carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbin örneği

Bu, döndürülen dizinin uzunluğu sağlanan diziyle aynı olacağından, harita ile aynı olacaktır. (Bu durumda a map, a'dan biraz daha ucuzdur reduce):

function getFields(list, field, otherwise) {
    //  map the provided list to an array containing either the requested field or the alternative value
    return list.map(function(item) {
        //  If item is an object and contains the field, add its value and the value of otherwise if not
        return typeof item === 'object' && field in item ? item[field] : otherwise;
    }, []);
}

jsbin örneği

Ve sonra en esnek çözüm, sadece alternatif bir değer sağlayarak her iki davranış arasında geçiş yapmanıza izin veren bir çözüm var.

function getFields(list, field, otherwise) {
    //  determine once whether or not to use the 'otherwise'
    var alt = typeof otherwise !== 'undefined';

    //  reduce the provided list to an array only containing the requested field
    return list.reduce(function(carry, item) {
        //  If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
        if (typeof item === 'object' && field in item) {
            carry.push(item[field]);
        }
        else if (alt) {
            carry.push(otherwise);
        }

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbin örneği

Yukarıdaki örnekler (umarım) bunun işleyişine biraz ışık tuttuğundan, işlevi kullanarak Array.concatişlevi biraz kısaltalım .

function getFields(list, field, otherwise) {
    var alt = typeof otherwise !== 'undefined';

    return list.reduce(function(carry, item) {
        return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
    }, []);
}

jsbin örneği


6

Genel olarak, bir dizinin içindeki nesne değerlerini tahmin etmek istiyorsanız (soruda açıklandığı gibi), azaltma, eşleme ve dizi yıkımını kullanabilirsiniz.

ES6

let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);

console.log(b)

For in loop kullanarak eşdeğer :

for (let i in a) {
  let temp = [];
  for (let j in a[i]) {
    temp.push(a[i][j]);
  }
  array.push(temp);
}

Üretilen çıktı: ["kelime", "tekrar", "bazı", "1", "2", "3"]


3

"Daha iyi" tanımınıza bağlıdır.

Diğer cevaplar, doğal (özellikle fonksiyonel stile alışkın olanlar için) ve özlü olan haritanın kullanılmasına işaret ediyor. (Birkaç IE8-IT adamlarla uğraşmazsanız) kullanmanızı şiddetle tavsiye ederim. Yani "daha iyi", "daha özlü", "sürdürülebilir", "anlaşılabilir" anlamına geliyorsa, evet, çok daha iyi.

Öte yandan, bu güzellik ek maliyetler olmadan gelmez. Ben büyük bir mikrobenç hayranı değilim, ama burada küçük bir test yaptım . Sonuç tahmin edilebilir, eski çirkin yol harita işlevinden daha hızlı görünüyor. Eğer "daha iyi" "daha hızlı" demekse, o zaman hayır, eski okul modasıyla kalın.

Yine bu sadece bir mikrobençtir ve hiçbir şekilde kullanımına karşı savunucu değildir map, bu sadece benim iki sentim :).


Peki, bu aslında bir tarayıcıda değil sunucu tarafı, yani IE8 önemsiz. Evet, mapgitmek için elverişli bir yoldur, bir şekilde google ile bulamadım (sadece jquery'nin haritasını buldum, bu alakalı değil).
hyde

3

Dizi benzeri nesneleri de desteklemek istiyorsanız, Array.from (ES2015) kullanın :

Array.from(arrayLike, x => x.foo);

Array.prototype.map () yöntemine göre avantajı girdinin de bir Set olabilmesidir :

let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);

2

ES6 + 'da birden fazla değer istiyorsanız aşağıdakiler çalışır

objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];

let result = objArray.map(({ foo, baz }) => ({ foo, baz }))

Bu {foo, baz}, solda nesne yıkıcı kullanıyor ve okun sağ tarafında ES6'nın gelişmiş nesne değişmezleri{foo: foo, baz: baz} nedeniyle eşdeğerdir .


tam ihtiyacım olan şey, sizce bu çözüm ne kadar hızlı / yavaş?
Ruben

1
@Ruben için gerçekten bu sayfadan çeşitli seçenekleri almak ve böyle bir şey kullanarak testler içine koymak gerekir düşünüyorum anlatmak jsperf.com
Chris Magnuson

1

Nesne dizileriyle çalışırken işlev haritası iyi bir seçimdir. Şimdiye kadar yayınlanmış bir dizi iyi yanıt olmasına rağmen, haritayı filtreyle birlikte kullanma örneği faydalı olabilir.

Hangi değerlerin tanımsız olduğu özellikleri veya yalnızca belirli bir özelliği hariç tutmak istiyorsanız, aşağıdakileri yapabilirsiniz:

    var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
    var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
    var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});

https://jsfiddle.net/ohea7mgk/


0

Yukarıda verilen cevap, tek bir özelliği ayıklamak için iyidir, ya nesne dizisinden birden fazla özelliği ayıklamak istiyorsanız. İşte çözüm !! Bu durumda _.pick (object, [paths])

_.pick(object, [paths])

ObjArray öğesinin aşağıdaki gibi üç özelliğe sahip nesneleri olduğunu varsayalım

objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];

Şimdi her nesneden foo ve bar özelliğini ayıklamak ve bunları ayrı bir dizide saklamak istiyoruz. Önce dizi öğelerini map kullanarak tekrarlayacağız ve daha sonra Lodash Library Standard _.pick () yöntemini uygulayacağız.

Artık 'foo' ve 'bar' özelliklerini çıkarabiliyoruz.

var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])}) console.log(newArray);

ve sonuç [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}] olur

zevk almak!!!


1
Nereden _.pickgeliyor? Standart bir işlev değildir.
neves

Cevabı yeni güncelledim, _.pick () Lodash kütüphanesinin standart bir yöntemidir.
Akash Jain

0

İç içe dizileriniz varsa, bu şekilde çalışmasını sağlayabilirsiniz:

const objArray = [ 
     { id: 1, items: { foo:4, bar: 2}},
     { id: 2, items: { foo:3, bar: 2}},
     { id: 3, items: { foo:1, bar: 2}} 
    ];

    let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
    
    console.log(result)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.