İçeriğe geç
academia.sh

Ders 12 / 17

Map ve Set

Nesneyi sözlük olarak kullanmanın sınırları, anahtar kimliğini koruyan eşleme türü, tekil değer kümeleri ve iki kuruluşun gruplama üzerinde karşılaştırılması.

İçindekiler

Kursun ilk dersinde prototipsiz nesne, sonraki derslerde de aynı önlem yeniden karşımıza çıktı: kalibrasyon tablosunda, not alma önbelleğinde ve gruplama birikecinde. Her seferinde neden aynıydı — anahtarları program dışından gelen bir eşlemede, nesnenin zincirinden gelen adlar sonucu bozuyordu.

Bu, nesneyi sözlük olarak kullanmanın tek sınırı değil. Anahtarlar dizgiye çevrilir, sıra kuralı beklenenden farklıdır ve eleman sayısı doğrudan okunamaz. Dil bu iş için iki tür sunar: anahtar–değer çiftleri için Map, tekil değerler için Set. Ders önce sınırları gösterir, sonra iki türü tanıtır.

Nesnenin Sözlük Olarak Sınırları

Nesne özelliklerinin anahtarı yalnızca dizgi ya da simge olabilir. Başka türden bir anahtar verildiğinde dizgiye çevrilir.

const sozluk = {};
const anahtarNesnesi = { sensor: "S-01" };
const digerNesne = { sensor: "S-02" };

sozluk[1] = "sayi bir";
sozluk["1"] = "dizgi bir";
sozluk[true] = "mantiksal";
sozluk[anahtarNesnesi] = "birinci nesne";
sozluk[digerNesne] = "ikinci nesne";

console.log(Object.keys(sozluk).join(" | "));
console.log(sozluk[1]);
console.log(sozluk[anahtarNesnesi]);
console.log(Object.keys(sozluk).length);

const sirali = {};
sirali["S-02"] = 1;
sirali[10] = 2;
sirali["S-01"] = 3;
sirali[2] = 4;
console.log(Object.keys(sirali).join(","));

const kirli = { sensor: "S-01" };
console.log("toString" in kirli);
console.log(Object.hasOwn(kirli, "toString"));
console.log(typeof kirli["toString"]);
1 | true | [object Object]
dizgi bir
ikinci nesne
3
2,10,S-02,S-01
true
false
function

Üç sınır görünür durumda. Birincisi, 1 ile "1" aynı anahtardır; ikinci atama birinciyi ezmiştir. İkincisi daha ağırdır: iki farklı nesne aynı "[object Object]" dizgisine çevrildiği için tek bir anahtar hâline gelir — nesneler anahtar olarak kullanılamaz. Üçüncüsü sıradır: tam sayıya benzeyen anahtarlar artan sayısal düzende öne alınır, diğerleri ekleme sırasını korur.

Son üç satır tanıdık sorunu yineler. Sözlükte toString diye bir anahtar yoktur ama sorgu onu bulur; anahtarların dışarıdan geldiği her durumda Object.hasOwn ya da prototipsiz nesne kullanılmalıdır.

Anahtar Kimliğini Koruyan Eşleme

Map, anahtarları dönüştürmez. Her tür değer anahtar olabilir ve karşılaştırma kimlik üzerinden yapılır.

const anahtarNesnesi = { sensor: "S-01" };
const digerNesne = { sensor: "S-02" };

const defter = new Map();
defter.set(1, "sayi bir");
defter.set("1", "dizgi bir");
defter.set(anahtarNesnesi, "birinci nesne");
defter.set(digerNesne, "ikinci nesne");
defter.set(NaN, "tanimsiz olcum");

console.log(defter.size);
console.log(defter.get(1));
console.log(defter.get("1"));
console.log(defter.get(anahtarNesnesi));
console.log(defter.get({ sensor: "S-01" }));
console.log(defter.get(NaN));
console.log(defter.has(digerNesne));

defter.delete(digerNesne);
console.log(defter.size);

const anahtarTurleri = [...defter.keys()].map((a) => typeof a).join(",");
console.log(anahtarTurleri);

const sirali = new Map();
sirali.set("S-02", 1);
sirali.set(10, 2);
sirali.set("S-01", 3);
sirali.set(2, 4);
console.log([...sirali.keys()].join(","));
5
sayi bir
dizgi bir
birinci nesne
undefined
tanimsiz olcum
true
4
number,string,object,number
S-02,10,S-01,2

1 ile "1" ayrı anahtarlardır. Nesne anahtarı kimliğiyle saklanır: aynı içerikli başka bir nesneyle sorgulandığında sonuç bulunamaz. Programlama Temelleri kursundaki Değişkenler ve Bağlama dersindeki kimlik–eşitlik ayrımı burada doğrudan işler.

NaN anahtarının bulunabilmesi bir ayrıntıya işaret eder: karşılaştırma katı eşitlikle değil, NaN değerini kendisine eşit sayan bir kuralla yapılır. Aynı kural Set için de geçerlidir.

Son satır sıra kuralını gösterir: Map yalnızca ekleme sırasını korur, sayısal anahtarları öne almaz. size özelliği eleman sayısını doğrudan verir; nesnede aynı bilgi için tüm anahtarların listelenmesi gerekir.

Karma tablosu ile ilişkisi de burada kurulur. Veri Yapıları kursundaki Karma Tabloları dersinde incelenen ortalama sabit maliyetli erişim, hem nesne özelliklerinin hem de Map yapısının altında yatan davranıştır; Map bunun anahtar kimliğini koruyan biçimidir.

Dönüşümler ve Gezinme

Map ile nesne arasında iki yönlü dönüşüm yerleşiktir. Dizileştirmede ise fark önemlidir.

const kayitNesnesi = { "S-01": 21.4, "S-02": 19.8, "S-03": 18.2 };

const nesnedenMap = new Map(Object.entries(kayitNesnesi));
console.log(nesnedenMap.size);
console.log(nesnedenMap.get("S-02"));

const geriNesne = Object.fromEntries(nesnedenMap);
console.log(JSON.stringify(geriNesne));

console.log(JSON.stringify(nesnedenMap));
console.log(JSON.stringify([...nesnedenMap]));

const ciftlerden = new Map([
  ["S-01", 21.4],
  ["S-02", 19.8],
]);
console.log([...ciftlerden.entries()].map(([a, d]) => `${a}=${d}`).join(" "));

let toplam = 0;
for (const [, deger] of nesnedenMap) toplam += deger;
console.log(toplam.toFixed(1));

nesnedenMap.forEach((deger, anahtar) => {
  if (anahtar === "S-01") console.log(`forEach: ${anahtar} -> ${deger}`);
});
3
19.8
{"S-01":21.4,"S-02":19.8,"S-03":18.2}
{}
[["S-01",21.4],["S-02",19.8],["S-03",18.2]]
S-01=21.4 S-02=19.8
59.4
forEach: S-01 -> 21.4

Dördüncü satır kritiktir: JSON.stringify bir Map nesnesini boş nesne olarak dizileştirir. Neden ilk derste kurulmuştu — dizileştirme kendi sayılabilir özelliklerle çalışır, Map içeriği ise özellik olarak tutulmaz. Verinin taşınması gerekiyorsa çiftler dizisine çevrilmelidir. Aynı sorunun genel çözümü kursun son dersinde ele alınacak.

for...of döngüsü her adımda bir çift verir; ayrıştırmayla anahtar ve değer ayrılabilir. forEach yönteminde argüman sırası ters olduğu için — önce değer, sonra anahtar — karıştırılmaya açıktır.

Tekil Değerler

Set, aynı değeri birden çok kez tutmayan bir koleksiyondur. Veri Yapıları kursundaki Kümeler ve Çok Kümeler dersinde tanıtılan kümenin dildeki karşılığıdır.

const okunanSensorler = ["S-01", "S-02", "S-01", "S-03", "S-02", "S-01"];

const tekiller = new Set(okunanSensorler);
console.log(tekiller.size);
console.log([...tekiller].join(","));
console.log(tekiller.has("S-02"));
console.log(tekiller.has("S-99"));

const sayiKumesi = new Set([1, 1, NaN, NaN, 0, -0]);
console.log(sayiKumesi.size);
console.log([...sayiKumesi].join(","));

const nesneKumesi = new Set([{ sensor: "S-01" }, { sensor: "S-01" }]);
console.log(nesneKumesi.size);

const bakimdakiler = new Set(["S-02", "S-04"]);
const kesisim = [...tekiller].filter((s) => bakimdakiler.has(s));
const fark = [...tekiller].filter((s) => !bakimdakiler.has(s));
const birlesim = [...new Set([...tekiller, ...bakimdakiler])];

console.log(kesisim.join(","));
console.log(fark.join(","));
console.log(birlesim.join(","));
3
S-01,S-02,S-03
true
false
3
1,NaN,0
2
S-02
S-01,S-03
S-01,S-02,S-03,S-04

Tekilleştirme tek satıra iner ve ekleme sırası korunur. Sayısal örnek karşılaştırma kuralını netleştirir: iki NaN değeri aynı sayılır — katı eşitlikte olmayan bir davranış — buna karşılık 0 ile -0 da aynı sayılır. Nesneler yine kimlikleriyle karşılaştırılır; aynı içerikli iki nesne ayrı elemanlardır.

Küme işlemleri, has sorgusunun ortalama sabit maliyetli olmasından yararlanır. Aynı işlemi dizilerle yapmak, her eleman için doğrusal arama gerektirir ve maliyeti iki kümenin boyutlarının çarpımına çıkarır.

Gruplamada Karşılaştırma

Yüksek Mertebeden Fonksiyonlar dersindeki gruplama örneği, iki kuruluşu yan yana koymak için uygun bir sınamadır. Sensör adları veri kaynağından geldiğine göre, aralarında constructor gibi bir ad da bulunabilir.

const kayitlar = [
  { sensor: "S-01", deger: 21.4 },
  { sensor: "S-02", deger: 19.8 },
  { sensor: "S-01", deger: 25.1 },
  { sensor: "constructor", deger: 0.5 },
];

function naifGrupla(liste) {
  const grup = {};
  for (const kayit of liste) {
    if (!grup[kayit.sensor]) grup[kayit.sensor] = [];
    grup[kayit.sensor].push(kayit.deger);
  }
  return grup;
}

function nesneyleGrupla(liste) {
  const grup = {};
  for (const kayit of liste) {
    if (!Array.isArray(grup[kayit.sensor])) grup[kayit.sensor] = [];
    grup[kayit.sensor].push(kayit.deger);
  }
  return grup;
}

function mapIleGrupla(liste) {
  const grup = new Map();
  for (const kayit of liste) {
    if (!grup.has(kayit.sensor)) grup.set(kayit.sensor, []);
    grup.get(kayit.sensor).push(kayit.deger);
  }
  return grup;
}

try {
  naifGrupla(kayitlar);
} catch (hata) {
  console.log(`naif surum: ${hata.constructor.name}`);
}

const nesneGrubu = nesneyleGrupla(kayitlar);
const mapGrubu = mapIleGrupla(kayitlar);

console.log(Object.keys(nesneGrubu).join(","));
console.log([...mapGrubu.keys()].join(","));
console.log(nesneGrubu["S-01"].join(","));
console.log(mapGrubu.get("S-01").join(","));
console.log(nesneGrubu["constructor"].join(","));
console.log(mapGrubu.get("constructor").join(","));
console.log(Object.keys(nesneGrubu).length === mapGrubu.size);
naif surum: TypeError
S-01,S-02,constructor
S-01,S-02,constructor
21.4,25.1
21.4,25.1
0.5
0.5
true

Naif sürüm hata verir: grup["constructor"] ifadesi zincirden gelen fonksiyonu bulur, bu değer doğru sayıldığı için dizi hiç yaratılmaz ve fonksiyona push çağrılır. Düzeltilmiş sürüm doğrulamayı tür denetimiyle yaptığı için çalışır — ama düzeltmenin gerekli olduğunu bilmek gerekir.

Map ile yazılan sürümde böyle bir tuzak yoktur; has sorgusu zincire bakmaz, çünkü Map içeriği özellik olarak tutulmaz. Kural olarak: anahtarlar sabit ve programcı tarafından yazılmışsa nesne, veri kaynağından geliyorsa Map kullanılır.

Özet

  • Nesne anahtarları dizgiye çevrilir; farklı nesneler aynı anahtara düşer ve tam sayıya benzeyen anahtarlar sıralamada öne alınır.
  • Map anahtarları dönüştürmez, kimlikleriyle saklar, ekleme sırasını korur ve size ile eleman sayısını doğrudan verir.
  • NaN değeri hem Map hem Set içinde kendisine eşit sayılır; nesneler kimlikleriyle karşılaştırılır.
  • JSON.stringify bir Map nesnesini boş nesne olarak dizileştirir; içerik çiftler dizisine çevrilmelidir.
  • Set tekilleştirmeyi tek adıma indirir; küme işlemleri sabit maliyetli sorgudan yararlanır.
  • Anahtarlar veri kaynağından geliyorsa Map, sabit ve programda yazılıysa nesne kullanılır.

Sonraki Adım

Map bir anahtarı sakladığı sürece, o anahtar nesnesi bellekte kalır. Bir nesneye ek bilgi iliştirmek için kurulan tablolar bu yüzden nesnelerin ömrünü uzatır: tablodan silinmeyen her giriş, artık kullanılmayan bir nesneyi yaşatır. Sonraki ders, tuttukları nesnelerin toplanmasını engellemeyen zayıf koleksiyonları ve bunların özel veri saklamada oynadığı rolü ele alacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat