paint-brush
Approaching Problems like a Software Engineerby@scrabill
904 reads
904 reads

Approaching Problems like a Software Engineer

by ShannonAugust 24th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

A recent mock technical interview asked a Software Engineer to write a function to solve a problem. The function takes in two inputs, an array, and a number. Remove the first item in the array, move it to the last place and return the array. Repeat the above for each time the second argument specified the number of times it should shuffle is 1, the function should return [1,2,3] or [3,2] MoveToEnd() is the same as a remainder of the original array with the same number.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Approaching Problems like a Software Engineer
Shannon HackerNoon profile picture

During a recent, mock technical interview, I was given the following problem to solve.

Write a function that takes in two inputs, an array, and a number. Remove the first item in the array. Then, move it to the last place in the array. Repeat this the number of times as given in the second argument and return the array.

For example, if the given array is 

[1,2,3]
, and the number of times it should shuffle is 1, the function should return 
[2,3,1]
.

Break Down the Problem

Given the problem, I thought about the main components (or problems) I would need to make things work.

  • Remove the first item in the area (the item at place 
    array[0]
    )
  • Take the removed item and place is at the end of the array (it becomes the item at place 
    array[array.length -1]
    .
  • Repeat the above for each time the second argument specified
  • Return the shuffled array

Knowing that the act of removing an item and placing it into the array would have to happen

x
numbers of times, a 
for
 loop seemed like a good place to start. Within that loop I could remove the first item in the array with 
.shift()
 then add it back onto the end of the array with 
.push()
.

Putting that together into a function, our solution could look like this:

function moveToEnd(array, number) {
	
	for ( let i = 0; i < number; i++ ) {
		
		let itemToMove = array.shift()
		array.push(itemToMove)
		
	}
		
	return array
	
}

Given the following inputs:

moveToEnd([1,2,3], 1)
moveToEnd([1,2,3], 2)
moveToEnd([1,2,3], 3)

We get the following outputs:

[2,3,1]
[3,1,2]
[1,2,3]

It works!

While this solution does work, it may not be the best way to approach the problem. Let’s think about some possible edge cases that we have not accounted for.

Run Time

In the sample code, I have three simple examples of what the inputs and outputs could look like. The array is relatively small, so have been the number of times the 

for
 loop needs to run.

In Javascript, an array can be any size. Shuffling through an array of 1000 items would take longer than shuffling through an array of 3 items. An array of 1,000,000 items would take even longer. Alternatively, if a 3 item array needed to be shuffled 1,000,000 times, it would take longer than removing an item from the front and placing it on the end 3 times.

Time complexity, runtime, and Big O notation are some of the concepts in computer science surrounding the idea of how fast, slow, or performant a program or function is. These concepts are too complex to cover in this blog post, however, we can keep the idea of runtime in mind as we refactor the 

moveToEnd
 function.

Noticing Patterns

Focusing on one aspect of the function, the user inputs, we can improve our solution a little bit.

Going back to our earlier examples, let’s shuffle the 

[1,2,3]
 array a few more times.

moveToEnd([1,2,3], 1) // expected return [2,3,1]
moveToEnd([1,2,3], 2) // expected return [3,1,2]
moveToEnd([1,2,3], 3) // expected return [1,2,3]
moveToEnd([1,2,3], 4) // expected return [2,3,1]
moveToEnd([1,2,3], 5) // expected return [3,1,2]
moveToEnd([1,2,3], 6) // expected return [1,2,3]

You may see a pattern here.

If the array is shuffled 1 time or 4 times, the resulting array (

[2,3,1]
) is the same. Each time 
number
 is incremented by 1, the resulting array is the same as every 3rd instance. Another interesting thing is that when the array is shuffled 3 times (or a multiple of 3 with 0 as a remainder) the resulting array is the same as the original array.

This is interesting to think about.

If the return of a function is the same when we shuffle the array 1 time, 3 times or 6,000,000 times, do we really need our for loop to run 6,000,000 times?

No.

We can make our code a little more performant by simplifying our 

number
 to be as small as possible in comparison to the 
array
 then running the 
for
 loop that many times.

Here are some scenarios to think about:

  • If number is less than then the length of the 
    array
     run the 
    for
     loop
  • If number is greater than length of the 
    array
    , divide it by the length of the array, then use the remainder to run the
    for
    loop

There are a few ways to tackle this, but for the purpose of this example, I’ll use an 

if
 statement to evaluate the value of the number argument.

if (number > array.length) {
		number = number % array.length
	}

On the second line, we use the modulus operator to return the remainder and assign it to the number variable, that is then used in the 

for
 loop.

Our updated function now looks like this.

function moveToEnd(array, number) {
		
	if (number > array.length) {
		number = number % array.length
	}	
		
	for ( let i = 0; i < number; i++ ) {
		
		let itemToMove = array.shift()
		array.push(itemToMove)
		
	}
		
        return (array)
	
}

With this addition, our 

for
 loop will run a relatively small number of times compared to what the number argument could be.

What else?

The 

moveToEnd
 problem is relatively easy to solve on purpose. Solving problems as an engineer is part of getting to a solution that works. And part getting to a solution that scales, accounts for edge cases or has minimal trade-offs or side effects.

In technical interviews, you may not have all the answers to what inputs could look like, what outputs should be, or other factors to consider. But, that’s on purpose. It’s on you, as the developer, to ask clarifying questions to help guide you to an optimal solution.

For example, when I was posed with this problem, I asked if it was ok to mutate the original array, or if I should return a new, shuffled array, leaving the original intact. If I was approaching this problem now, I may think about if my solution works with a nested array. Or, to account for if the number argument is not an integer or less is less than 0. Going a little deeper, I may revisit the 

for
 loop to see if there is a better option.

Conclusion

Again, moving items in an array may not be a scenario you will encounter in the real world. But, thinking about making your code efficient is something you will encounter. Challenge yourself to revisit code you have written and refactor it to account for less-than-ideal user inputs, time complexity any other edge cases you can think of.

Cover photo by Olav Ahrens Røtne on Unsplash

Previously published at https://shannoncrabill.com/blog/approaching-problems-like-a-software-engineer/