HTML loop in JavaScript

User
We have javascript array with data
tickets
:
Array(2)
0
:
{section: 'tiered', type: 'ADULT', price: 18, quantity: 2}
1
:
{section: 'tiered', type: 'CHILD', price: 14, quantity: 1}

now I have a event loop
each event has section,type and price
Now I want to fill quantity if its avaul tickets array otherwise set it 0 based match section,type and price

Assistant
Got it 👍 You want to **merge your `tickets` array with event tickets**, so that each event has a `quantity` (taken from `tickets` if it exists, otherwise `0`).

Here’s a clean way to do it:

const tickets = [
  { section: 'tiered', type: 'ADULT', price: 18, quantity: 2 },
  { section: 'tiered', type: 'CHILD', price: 14, quantity: 1 }
];

const events = [
  { section: 'tiered', type: 'ADULT', price: 18 },
  { section: 'tiered', type: 'CHILD', price: 14 },
  { section: 'tiered', type: 'SENIOR', price: 12 },
  { section: 'vip', type: 'ADULT', price: 30 }
];

// Map tickets for quick lookup
const ticketMap = new Map(
  tickets.map(t => [`${t.section}_${t.type}_${t.price}`, t.quantity])
);

const merged = events.map(ev => {
  const key = `${ev.section}_${ev.type}_${ev.price}`;
  return {
    ...ev,
    quantity: ticketMap.get(key) || 0
  };
});

console.log(merged);

✅ Output:

[
  { section: 'tiered', type: 'ADULT', price: 18, quantity: 2 },
  { section: 'tiered', type: 'CHILD', price: 14, quantity: 1 },
  { section: 'tiered', type: 'SENIOR', price: 12, quantity: 0 },
  { section: 'vip', type: 'ADULT', price: 30, quantity: 0 }
]

This way, if a match exists in `tickets`, quantity comes from it; otherwise, it defaults to `0`.

👉 Do you also want to handle **cases where section/type matches but price differs slightly** (like float rounding), or should it be an **exact match** only?

User
Not want to merege i want to put in javascript define html loop
Assistant
Got it—set the `quantity` while you’re **building the HTML**. Two easy patterns:

### 1) Fast (precompute a lookup, then use in your loop)

const tickets = [
  { section: 'tiered', type: 'ADULT', price: 18, quantity: 2 },
  { section: 'tiered', type: 'CHILD', price: 14, quantity: 1 }
];

const events = [
  { section: 'tiered', type: 'ADULT', price: 18 },
  { section: 'tiered', type: 'CHILD', price: 14 },
  { section: 'tiered', type: 'SENIOR', price: 12 },
  { section: 'vip', type: 'ADULT', price: 30 }
];

// Build a quick lookup once
const keyOf = (obj) => `${obj.section}|${obj.type}|${Number(obj.price)}`;
const ticketMap = new Map(tickets.map(t => [keyOf(t), t.quantity]));

// Build HTML
let html = '';
for (const ev of events) {
  const qty = ticketMap.get(keyOf(ev)) ?? 0;
  html += `
    <div class="ticket-row" data-section="${ev.section}" data-type="${ev.type}" data-price="${ev.price}">
      <span class="sec">${ev.section}</span>
      <span class="type">${ev.type}</span>
      <span class="price">${ev.price}</span>
      <input type="number" class="qty" min="0" value="${qty}">
    </div>
  `;
}

document.getElementById('tickets-list').innerHTML = html;

### 2) Simple (find inside the loop)

let html = '';
for (const ev of events) {
  const found = tickets.find(t =>
    t.section === ev.section &&
    t.type === ev.type &&
    Number(t.price) === Number(ev.price)
  );
  const qty = found?.quantity ?? 0;

  html += `
    <div class="ticket-row">
      <span>${ev.section}</span>
      <span>${ev.type}</span>
      <span>${ev.price}</span>
      <input type="number" class="qty" min="0" value="${qty}">
    </div>
  `;
}

document.getElementById('tickets-list').innerHTML = html;

> Tip: If prices can be floats/strings, normalize with `Number()` (or `toFixed(2)`) in both places so keys match reliably.

User
console.log('Current orderData if page referesh:', orderData.tickets);
// Update order info block
const orderInfoBlock = document.querySelector('.order-info-block');
if (orderInfoBlock) {
orderInfoBlock.innerHTML = `
Order Information

${eventData.event_date_string}

${eventData.venue_title}

${eventData.venue_full}

`;
}

const html = eventData.tickets.map(section => {
const sectionId = section.title.toLowerCase().replace(/s+/g, '_');

return `

${section.title}
${section.seating_type}

Ticket Type
Price Per Person
Number of Tickets*

${section.ticket_types.map(type => `

${type.label}

Online Special
€${type.price}

+

`).join('')}

`}).join('');

Assistant
You can prefill the quantity right in your HTML build by using a small lookup helper.

Drop these helpers **before** your `const html = …`:

// Build a quick lookup from existing orderData.tickets
const keyOf = (sectionId, label, price) => `${String(sectionId).toLowerCase()}|${String(label)}|${Number(price)}`;
const qtyMap = new Map(
  (orderData.tickets || []).map(t => [keyOf(t.section, t.type, t.price), Number(t.quantity) || 0])
);
const getQty = (sectionId, label, price) => qtyMap.get(keyOf(sectionId, label, price)) ?? 0;

Then use `getQty(…)` when rendering each ticket input:

const html = eventData.tickets.map(section => {
  const sectionId = section.title.toLowerCase().replace(/s+/g, '_');

  return `
    <div class="seating-section">
      <span class="seating-title ${section.css_class}">
        <h2>${section.title}</h2>
        <p class="seating-type">${section.seating_type}</p>
      </span>

      <div class="ticket-grid">
        ${section.ticket_types.map(type => `
          <div class="ticket-type">
            <div class="ticket-type-title">${type.label}</div>
            <div class="price-deets">
              <div class="price-label">Online Special</div>
              <div class="price" style="width: 30%;">€${type.price}</div>
            </div>
            <div class="ticket-quantity">
              <div class="quantity-selector">
                <button type="button" class="quantity-btn minus ticket-quantity-btn"
                        data-section="${sectionId}" data-ticket-type="${type.label}" data-price="${type.price}">-</button>

                <input type="number" class="quantity-value"
                       data-section="${sectionId}" data-ticket-type="${type.label}" data-price="${type.price}"
                       value="${getQty(sectionId, type.label, type.price)}"
                       min="0" max="${type.seats_left || 10}" readonly>

                <button type="button" class="quantity-btn plus ticket-quantity-btn"
                        data-section="${sectionId}" data-ticket-type="${type.label}" data-price="${type.price}">+</button>
              </div>
            </div>
          </div>
        `).join('')}
      </div>
    </div>
  `;
}).join('');

This matches by `sectionId` (derived from the section title), `type.label`, and `price`. If no match is found in `orderData.tickets`, the input defaults to `0`.

User
also apply check if orderData.tickets is available or not
Assistant
Perfect 👍 You just need a safe check before you build your lookup map.
Here’s the full updated version with the `orderData.tickets` check:

// Helper to build a key for matching
const keyOf = (sectionId, label, price) =>
  `${String(sectionId).toLowerCase()}|${String(label)}|${Number(price)}`;

// Build qtyMap only if tickets exist
let qtyMap = new Map();
if (orderData && Array.isArray(orderData.tickets) && orderData.tickets.length > 0) {
  qtyMap = new Map(
    orderData.tickets.map(t => [
      keyOf(t.section, t.type, t.price),
      Number(t.quantity) || 0
    ])
  );
}

// Getter to use inside your loop
const getQty = (sectionId, label, price) =>
  qtyMap.get(keyOf(sectionId, label, price)) ?? 0;

Then in your HTML loop, just use:

<input type="number" class="quantity-value"
       data-section="${sectionId}"
       data-ticket-type="${type.label}"
       data-price="${type.price}"
       value="${getQty(sectionId, type.label, type.price)}"
       min="0" max="${type.seats_left || 10}" readonly>

✅ If `orderData.tickets` is missing or empty → all values default to `0`.
✅ If `orderData.tickets` is present → quantities will prefill correctly.

Do you want me to also make it so **if `orderData.tickets` is not defined at all** (like `undefined`), it skips building the map entirely?

User
quantity still shows 0 even in ticket it is 2
Assistant
If it’s still `0`, your key isn’t matching. Most common culprits:

– `sectionId` you render (from `section.title`) ≠ `orderData.tickets[i].section` (e.g. `'tiered_seating'` vs `'tiered'`)
– `type.label` casing/spacing differs from `tickets[i].type`
– `price` is a string vs number (or has `€`)

Use a **stable normalizer** on both sides and a tolerant lookup:

// ---- Normalizers ----
const normalizeSection = (s) =>
  String(s || '')
    .toLowerCase()
    .replace(/[^w]+/g, '_')              // spaces & punctuation -> _
    .replace(/^_+|_+$/g, '');             // trim underscores

const normalizeType = (s) => String(s || '').trim().toUpperCase();

const normalizePrice = (p) => {
  // strip symbols & commas, convert to number
  const n = Number(String(p).replace(/[^d.-]/g, ''));
  return Number.isFinite(n) ? n : 0;
};

// composite key
const keyOf = (sectionKey, type, price) =>
  `${normalizeSection(sectionKey)}|${normalizeType(type)}|${normalizePrice(price)}`;

// ---- Build qty map from orderData (safe) ----
let qtyMap = new Map();

if (orderData && Array.isArray(orderData.tickets) && orderData.tickets.length) {
  // build multiple candidate keys per ticket to survive different "section" sources
  const entries = [];
  for (const t of orderData.tickets) {
    const sec = t.section; // e.g. "tiered"
    const typ = t.type;    // e.g. "ADULT"
    const pri = t.price;   // e.g. 18

    // primary key (as stored)
    entries.push([keyOf(sec, typ, pri), Number(t.quantity) || 0]);

    // also add a forgiving variant with underscores (if someone used title in UI)
    entries.push([keyOf(normalizeSection(sec), typ, pri), Number(t.quantity) || 0]);
  }
  qtyMap = new Map(entries);
}

// ---- Getter with tolerant fallback ----
const getQty = (sectionKey, typeLabel, price) => {
  // 1) fast path via map
  const k = keyOf(sectionKey, typeLabel, price);
  if (qtyMap.has(k)) return qtyMap.get(k);

  // 2) tolerant search if map miss (handles slight price/label/section differences)
  if (orderData && Array.isArray(orderData.tickets)) {
    const targetSec = normalizeSection(sectionKey);
    const targetType = normalizeType(typeLabel);
    const targetPrice = normalizePrice(price);

    const found = orderData.tickets.find(t =>
      normalizeSection(t.section) === targetSec &&
      normalizeType(t.type) === targetType &&
      Math.abs(normalizePrice(t.price) - targetPrice) < 0.001 // float-safe
    );
    if (found) return Number(found.quantity) || 0;
  }

  return 0;
};

Now, when building your HTML, **use a section key that actually matches your stored data**. If your stored `tickets[i].section` is like `'tiered'`, don’t derive it from the title—prefer a stable field like `section.css_class` or `section.slug`. Example:

const html = eventData.tickets.map(section => {
  // Prefer a stable key; fall back to title if needed
  const sectionKey = section.css_class || section.slug || section.title;

  return `
    <div class="seating-section">
      <span class="seating-title ${section.css_class}">
        <h2>${section.title}</h2>
        <p class="seating-type">${section.seating_type}</p>
      </span>

      <div class="ticket-grid">
        ${section.ticket_types.map(type => `
          <div class="ticket-type">
            <div class="ticket-type-title">${type.label}</div>
            <div class="price-deets">
              <div class="price-label">Online Special</div>
              <div class="price" style="width: 30%;">€${type.price}</div>
            </div>
            <div class="ticket-quantity">
              <div class="quantity-selector">
                <button type="button" class="quantity-btn minus ticket-quantity-btn"
                        data-section="${normalizeSection(sectionKey)}"
                        data-ticket-type="${type.label}"
                        data-price="${type.price}">-</button>

                <input type="number" class="quantity-value"
                       data-section="${normalizeSection(sectionKey)}"
                       data-ticket-type="${type.label}"
                       data-price="${type.price}"
                       value="${getQty(sectionKey, type.label, type.price)}"
                       min="0" max="${type.seats_left || 10}" readonly>

                <button type="button" class="quantity-btn plus ticket-quantity-btn"
                        data-section="${normalizeSection(sectionKey)}"
                        data-ticket-type="${type.label}"
                        data-price="${type.price}">+</button>
              </div>
            </div>
          </div>
        `).join('')}
      </div>
    </div>
  `;
}).join('');

### Quick debug (optional)
Add this once to see why it mismatches:

console.log('qtyMap keys:', [...qtyMap.keys()]);
// inside the map for each `type`:
// console.log('lookup key:', keyOf(sectionKey, type.label, type.price));

If you see `qtyMap` has `tiered|ADULT|18` but your lookup is `tiered_seating|ADULT|18`, switch your `sectionKey` source to match what’s in `orderData.tickets` (e.g., use `section.css_class` = `'tiered'`).

User
ignore case in section and type
Assistant
Got it 👍 If you want to **ignore case completely** for `section` and `type`, just normalize both sides to lowercase (or uppercase) before matching.

Here’s a clean version tailored for your case:

// ---- Normalizers ----
const normalizeSection = (s) =>
  String(s || '').trim().toLowerCase().replace(/[^w]+/g, '_');

const normalizeType = (s) =>
  String(s || '').trim().toLowerCase();

const normalizePrice = (p) =>
  Number(String(p).replace(/[^d.-]/g, ''));

// composite key
const keyOf = (sectionKey, type, price) =>
  `${normalizeSection(sectionKey)}|${normalizeType(type)}|${normalizePrice(price)}`;

// ---- Build qty map from orderData (safe) ----
let qtyMap = new Map();
if (orderData && Array.isArray(orderData.tickets) && orderData.tickets.length) {
  qtyMap = new Map(
    orderData.tickets.map(t => [
      keyOf(t.section, t.type, t.price),
      Number(t.quantity) || 0
    ])
  );
}

// ---- Getter ----
const getQty = (sectionKey, typeLabel, price) =>
  qtyMap.get(keyOf(sectionKey, typeLabel, price)) ?? 0;

### Usage in your HTML loop

<input type="number" class="quantity-value"
       data-section="${sectionId}"
       data-ticket-type="${type.label}"
       data-price="${type.price}"
       value="${getQty(sectionId, type.label, type.price)}"
       min="0" max="${type.seats_left || 10}" readonly>

✅ Now `tiered`, `TIERED`, `Tiered` all match.
✅ `adult`, `ADULT`, `Adult` also match.
✅ Price is still an exact number match.

Do you also want me to make **price tolerant** (e.g. `"18.00"` vs `18` vs `"€18"`), or should it stay exact number equality?

Leave a Comment

Your email address will not be published. Required fields are marked *