Can see sunshine?

We will check if building can see sunshine

·

6 min read

Table of contents

No heading

No headings in the article.

Hi!

This time I will try to create visual representation of the function checking if given building can see sunshine. The rules are simple.

Rules

We have number of buildings next to each other. We check every building and if there is any building to its west (left side) that is higher or equal with height, this builidng will not se sunshine. If there is no taller buldings to its west, this building will see sunshine.

It will be easier to understand with visual representation.

canseesunshine.png

As you can see first buidling will always see sunshine. Second in this case also because its higher that the first one. Third in this example will not see sunshine because it has same height building to the left and so forth.

I will also add color representation as in given example, so green means can see sunshine, red no sunshine.

Code

I was considering two approaches, first was just creating array of numbers representing block heights, and then working on that.

var blocks = [10,20,10,20,10,30,10]

And it is also possible to use this approach, I may even say it is simplier in terms of checking and comparing other blocks. But it was getting a bit messy in terms of displaying this to the user.

So I decided to try and use array of objects, this allowed me to store more information about given building and also divide my functions into cheking function and displaying function.

var blocksObj = [
  {height: 10, sunshine: true, color: 'red'},
  {height: 20, sunshine: true, color: 'red'},
  {height: 30, sunshine: true, color: 'red'},
  {height: 20, sunshine: true, color: 'red'},
  {height: 40, sunshine: true, color: 'red'},
  {height: 10, sunshine: true, color: 'red'},
  {height: 20, sunshine: true, color: 'red'}]

Here when I initialized this array of objects I declared default parameter for sunshine: true and for color: red, it will be important for the start but later it will not matter as I will explain later.

Then I started to implement the logic. As I mentioned above, first element will always see the sunshine. So we can set parameter values of our first element as sunshine: true and color: green.

As for main logic, we will use two loops. One loop will jump through all elements, starting from the second element. Second loop will jump backwards from element that we currently check.

Inside second loop we check if any element before has higher height value. If yes we assign sunshine parameter as false. Outside second loop when we are done checking the height of elements we already know if building will see sunshine or not. So we can change our color parameter to green if our sunshine parameter is true.

function checkSunshine(array){
  array[0].sunshine = true
  array[0].color = 'green'

  for(let j=1; j<array.length; j++){
    for(let i=j-1; i>=0; i--){
      if (array[j].height <= array[i].height) array[j].sunshine = false
    }     
    if(array[j].sunshine == true) array[j].color = 'green'
  }  
return array 
}

And thats it for our logic for now. Next we will try to display it to the user.

We will need simple HTML markup.

<div class="container">
    <div class="panel" id="build"></div>    
  </div>

Displaying buildings

To display our array of buildings I will use just div elements. Inside new function we will loop through elements and generate HTML markup. I used block.height *10 just for better visualization. Finally we can assign our markup to our div with id build.

let builds = document.getElementById('build')

function displaySunshine(array){
  let html = ``
  array.forEach(block => {
    html +=`<div 
              class="block" 
              style="height: ${block.height *10 }px; 
              background-color: ${block.color}">
              ${block.height}m
            </div>`
  })
  builds.innerHTML = html
}
displaySunshine(checkSunshine(blocksObj))

To display it properly we will need also two css classes.

CSS

.panel{
  height: 70vh;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.block{
  background-color: aquamarine;
  margin: 5px;
  width: 50px;
  text-align: center;
  padding-top: 10px;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: bold;
}

Now our result should look like this.

canseesunshine1.png

But this was not enough for me. I wantet to create inputs for user to change height of given elements.

User inputs

So I added inputs to my HTML markup. Also with min and max values for our visualization to work properly.

<div class="container">
    <div class="panel" id="build"></div>
    <div class="controls">
      <input type="number" id="b0" placeholder="meter" min="1" max="50">
      <input type="number" id="b1" placeholder="meter" min="1" max="50">
      <input type="number" id="b2" placeholder="meter" min="1" max="50">
      <input type="number" id="b3" placeholder="meter" min="1" max="50">
      <input type="number" id="b4" placeholder="meter" min="1" max="50">
      <input type="number" id="b5" placeholder="meter" min="1" max="50">
      <input type="number" id="b6" placeholder="meter" min="1" max="50">
    </div>
  </div>

And also two CSS classes to align everything with our buildings.

.controls{
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.controls input{
  height: 20px;
  width: 50px;
  padding: 0 !important;
  margin: 3px;
}

And this is how it should look until now.

canseesunshine2.png

Input logic

So before we even start with new logic, we need to go back to our checkSunshine() function. If user will change height parameter value, our check function will not work properly without default initialization of the sunshine and color value.

This is why I wrote when we initialized our array that values matter, but now with this two lines of code they do not matter.

function checkSunshineObj(array){
  array[0].sunshine = true
  array[0].color = 'green'

  for(let j=1; j<array.length; j++){
    array[j].sunshine = true
    array[j].color = 'red'

    for(let i=j-1; i>=0; i--){
      if (array[j].height <= array[i].height) array[j].sunshine = false
    }     
    if(array[j].sunshine == true) array[j].color = 'green'
  }  
return array 
}

So we can start our input logic. Firstly we will need all our inputs.

Then we will need to add addEventListener() to every input that will detect change made by user.

const inputs = document.querySelectorAll('#b0, #b1, #b2, #b3, #b4, #b5, #b6')

inputs.forEach(input => {
  input.addEventListener('change', updateHeight)
});

Now we will create function updateHeight. This function will take event as a parameter.

We need to declare two variables.

Id which is indication which element user clicked. I used here target id and took last character from it as we have numbers from 0 to 6.

And second variable is user input, we just read the input that user entered.

I added simple if statement to prevent user input any values higher than 50 and lower than 1.

Then we just udpate in our array object which is on position as our id and we assign height value to userinput.

Lastly we call again our check and display functions, to check all heights and display it to the user.

function updateHeight(e) {
  let id = e.target.id.slice(-1)
  let userinput = e.target.value
  if(userinput <= 0 || userinput > 50) return alert("please put number between 1 and 50")
  blocksObj[id].height = userinput
  displaySunshine(checkSunshine(blocksObj))
}

And now everything should work. We can check different heights and we can experiment with is to see cool efects.

If you find any mistakes or you have ideas how to refactor my solutions to be simplier/ better please let me know :)

Did you find this article valuable?

Support knowledge blog by becoming a sponsor. Any amount is appreciated!