Help on how to loop through this json please

I have now managed to get my previous json split by month and into a new JSON with help and guidance from members. I am now stuck on how to loop through it. All of the methods I used on the previous JSON are returning errors. Here is a shortened extract of the json:


"Dec '19":[
{
"hash":"820eRWiyq2Vy4Sk",
"arrival":"2019-12-01",
"departure":"2019-12-08",
"description":"",
"col1":"695",
"availability":"empty",
"availableRooms":"7",
"availableGuests":"16"
},
{
"hash":"8201ahPFdE28VUF",
"arrival":"2019-12-08",
"departure":"2019-12-15",
"description":"",
"col1":"695",
"availability":"empty",
"availableRooms":"7",
"availableGuests":"16"
},

"Jan '20":[
{
"hash":"820RDoYPKxtdkMK",
"arrival":"2020-01-05",
"departure":"2020-01-12",
"description":"",
"col1":"695",
"availability":"empty",
"availableRooms":"7",
"availableGuests":"16"
},
{

I think part of the problem is that the keys, in this case “Dec '19”, “Jan '20” etc are not known in advance as they are dynamically created from the first JSON (extract below):

myObjAll1= [
  {
    hash: "820eRWiyq2Vy4Sk",
    arrival: "2019-12-01",
    departure: "2019-12-08",
    description: "",
    col1: "695",
    availability: "empty",
    availableRooms: "7",
    availableGuests: "16"
  },
  {
    hash: "8201ahPFdE28VUF",
    arrival: "2020-01-13",
    departure: "2020-01-19",
    description: "",
    col1: "695",
    availability: "empty",
    availableRooms: "7",
    availableGuests: "16"
  },

returned via an API from a 3rd party company which have then been split by month using this:

function group_by_month(myObjAll1) {
var monthName = new Array();
  monthName[0] = "Jan";
  monthName[1] = "Feb";
  monthName[2] = "Mar";
  monthName[3] = "Apr";
  monthName[4] = "May";
  monthName[5] = "Jun";
  monthName[6] = "Jul";
  monthName[7] = "Aug";
  monthName[8] = "Sep";
  monthName[9] = "Oct";
  monthName[10] = "Nov";
  monthName[11] = "Dec";
var months = {}
    for (var i=0; i<myObjAll1.length; i++) {
       var obj = myObjAll1[i];
       var date = new Date(obj.arrival);
       var month = monthName[date.getMonth()] + " '" + [date.getYear().toString().substr(-2)];
//        console.log(month);    
       if (months[month]) {
           months[month].push(obj);  // already have a list- append to it
       }
       else {
           months[month] = [obj]; // no list for this month yet - create a new one
       }
    }
    return months;
 }

there are probably better ways of doing this but this is one way i had to remove the ' from your date number for this to work, you might be able to implement this method somehow

let jsonObj = {
  "Dec 19":[
{
"hash":"820eRWiyq2Vy4Sk",
"arrival":"2019-12-01",
"departure":"2019-12-08",
"description":"",
"col1":"695",
"availability":"empty",
"availableRooms":"7",
"availableGuests":"16"
},
{
"hash":"8201ahPFdE28VUF",
"arrival":"2019-12-08",
"departure":"2019-12-15",
"description":"",
"col1":"695",
"availability":"empty",
"availableRooms":"7",
"availableGuests":"16"
}],

"Jan 20":[
{
"hash":"820RDoYPKxtdkMK",
"arrival":"2020-01-05",
"departure":"2020-01-12",
"description":"",
"col1":"695",
"availability":"empty",
"availableRooms":"7",
"availableGuests":"16"
}
]
}

let objKeys = Object.keys(jsonObj)

objKeys.forEach(key => {
  console.log(jsonObj[key])
})

FWIW there now exists the Object.entries method which lets you iterate over keys and values of an object together

for example:

Object.entries(jsonObj)
    .forEach(([dateString, roomInformationList]) => doSomethingWith(dateString, roomInformationList));
1 Like

Thank you for that nugget of so far undiscovered wisdom. To aid my learning, how does it know what dateString and roomInformationList are when they haven’t been defined? It is a rather wonderful piece of code that I will be playing with until late this evening to get it to work with what I have.

Thank you for taking the time to look. I wonder if it is browser dependant as the ’ worked fine in Chrome testing?

1 Like

Object.entries(object) returns an array of key value pairs, so it’s whatever the keys and values are in the object

remember that you can use higher order functions like map, reduce, and foreach on arrays

dateString and roomInformationList are defined on every iteration of the forEach loop. This means, they change dynamically based on the object it is currently in.

PS: While plain JavaScript has a lot to offer, when you start heavily working with collections of data, I’d like to recommend Lodash.

https://lodash.com

OK, I am starting to understand it a little now.

Previously I used

${jsonObj.map(function(chalet) {
return `                              
<div class="row-days">
<div class="row-days-wrap">
<div class="day">
<span class="d">${chalet.arrival.slice(-2)}</span>
<span class="rooms-left">${chalet.availableRooms}</span>
<span class="price"><span class="only-price"><b>N/A</b></span><span class="inc-price">${price(chalet.col1)}</span></span>
<small class="was text-red"><span class="only-price"></span><span class="inc-price"></span></small>
<a style="${link(chalet.availableRooms)}" data-chalet-id="${chalet_ID}" data-chalet-name="${name}" data-chalet-offer-day="${chalet.arrival}" href="#" data-price="inc-price" data-toggle="modal" data-modal-class="modal-vertical-centered" data-modal-size="lg" data-target="#offer-enquiry-modal" class="btn btn-yellow"><i class="fa fa-angle-right"></i><i class="fa fa-angle-right"></i></a>
</div>
</div>
</div>
`
}).join('')}

To get the mapped data into my div but I am struggling how translate that over to use with

const map = new Map(Object.entries(jsonObj));
console.log(map);

I tried

console.log(map.availableRooms);
console.log(map.entries.availableRooms);
console.log(availableRooms);
console.log(entries.availableRooms);
console.log(map.Object.entries.availableRooms);
console.log(Object.entries.availableRooms);

but all returned undefined.

The ultimate aim is for the output to resemble this:


Which I have managed to create but have to manually code the month and then map that months availability which is fine until the months change. Thanks again

Are you sure you want a Map object?

The array method .map() and the data structure you get from new Map() are not the same thing. With the Map object you use get and set (myMapObject.set('key', 'value') / myMapObject.get('key')).

BTW, you may also want to check out toDateString, If you split the string making the month/year variable should be a bit cleaner.

Example:

const date = new Date(obj.arrival).toDateString().split(' ');
const monthYear = `${date[1]} ${date[3].substr(2)}`;

Finally got it to where I needed it. Here is my final code and thanks again for all the ideas that were provided to help me get there:

function room(availableRooms) {
	if (availableRooms == null) {
		return 'Full'
	} else if (availableRooms == 1) {
		return '1 Room Left'
	} else {
		return `${availableRooms} Rooms Left`
	}
}

function price(col1) {
	if (col1 == '') {
		return '<span class="price"><div style="color:white; background-color: #c92733; text-align: center;"><b>FULLY-BOOKED</b></div></span>'
	} else {
		return `<b>£${col1}</b> Per Person`
	}
}
Object.entries(jsonObj)
	.forEach(([dateString, roomInformationList]) => {
		var tbody = document.getElementById('app');
		var rowPrice = document.createElement('div');
		var rowDate = document.createElement('div');
		rowPrice.className = "chalet-price-row";
		rowDate.className = "row-date";
		rowDate.innerHTML = "<span><span>" + dateString + "</span></span>";
		tbody.appendChild(rowPrice);
		rowPrice.appendChild(rowDate);
		let chars = jsonObj[dateString];
		for (let i = 0, len = chars.length; i < len; i++) {
			var rowDays = document.createElement('div');
			rowDays.className = "row-days";
			rowDays.innerHTML = `<div class="row-days-wrap">
<div class="day">
<span class="d">${chars[i].arrival.slice(-2)}</span>
<span class="rooms-left">${room(chars[i].availableRooms)}</span>
<span class="price"><span class="only-price"><b>N/A</b></span><span class="inc-price">${price(chars[i].col1)}</span></span>
<small class="was text-red"><span class="only-price"></span><span class="inc-price"></span></small>
<a style="" data-chalet-id="26535" data-chalet-name="${chalet_ID}" data-chalet-offer-day="${chars[i].arrival}" href="#" data-price="inc-price" data-toggle="modal" data-modal-class="modal-vertical-centered" data-modal-size="lg" data-target="#offer-enquiry-modal" class="btn btn-yellow"><i class="fa fa-angle-right"></i><i class="fa fa-angle-right"></i></a>
</div>
</div>
</div>`;
			rowPrice.appendChild(rowDays);
		}
	});

I could be wrong, but it appears you have an extra closing div tag (</div>) in the for loop. Once way to see this is to indent your code differently to make it more readable.

for (let i = 0, len = chars.length; i < len; i++) {
  var rowDays = document.createElement('div');
  rowDays.className = "row-days";
  rowDays.innerHTML = `
    <div class="row-days-wrap">
      <div class="day">
        <span class="d">${chars[i].arrival.slice(-2)}</span>
        <span class="rooms-left">${room(chars[i].availableRooms)}</span>
        <span class="price">
          <span class="only-price"><b>N/A</b></span>
          <span class="inc-price">${price(chars[i].col1)}</span>
        </span>
        <small class="was text-red">
          <span class="only-price"></span>
          <span class="inc-price"></span>
        </small>
        <a style="" data-chalet-id="26535" data-chalet-name="${chalet_ID}" data-chalet-offer-day="${chars[i].arrival}" href="#" data-price="inc-price" data-toggle="modal" data-modal-class="modal-vertical-centered" data-modal-size="lg" data-target="#offer-enquiry-modal" class="btn btn-yellow">
          <i class="fa fa-angle-right"></i><i class="fa fa-angle-right"></i>
        </a>
      </div>
    </div>`;
  rowPrice.appendChild(rowDays);
}

I do believe you are correct! Strangely, it didn’t break the display or throw any errors. Is this to be expected?

An extra closing div would not necessarily cause an error, but if an extra closing div was put in the wrong place, it could cause things to look different than expected. In this case, it was not an issue. Using an HML validator would have found this problem.

I will take your advice once I have cleaned all the commented out lines that found their way in whilst testing.

I have to say, one of the biggest barriers to learning was not knowing what the parts of code were called so that I could search for tutorials on them. I found out about nodes,objects and other bits the hard, slow way.