Is this a good enough answer?
This isn't meaningfully answerable without understanding both your skill level and the expected skill level you're interviewing for, and any possible modifiers/priorities you've been given to solve this exercise.
Because I know people can solve it using Linq with less lines of code
Line count is not the main measure of quality. If anything, the LINQ example "cheats" by inlining a lot of different steps and ends up with compromised readability because of it.
That being said, there are certain improvements to be made here. You've made things much more complex than they need to be. I'm going to make an educated guess here and say that you were shotgun coding and then did not clean up/refactor afterwards.
You would've been better off taking a step back and figuring out if you could calculate some things instead of iteratively needing to exploring them. If you had done so, you would've been able to break things down into three easy steps.
The main example of this is that you're adding every whitespace individually, which is not performant. It could've been calculated once, instead of iteratively adding them. A basic formula would be:
amount_of_blanks_on_one_side = tower_height - current_step_index - 1
Or if your steps are counted using 1-indexing (which I will actually use in this case:
amount_of_blanks_on_one_side = tower_height - current_step
Pick any tower size, and you'll see that this formula is correct for every step of every possible tower.
I'm not going to get into the specific code for this yet, but this already suggests that you have a clearly abstractable method here:
string stepString = GenerateStep(towerHeight, currentStep);
We've now split our logic into two separate steps:
- How we generate a single step (
GenerateStep
)
- How we combine these single steps into a tower
Let's focus on the second one now. It is assume that we already know the tower height, so let's work with a basic variable for now:
int towerHeight = 3;
Then, we're going to need a list of the steps (i.e. the number denoting each step. That is always going to be a list of numbers starting at 1
and ending at towerHeight
, which can be easily generated using:
var steps = Enumerable.Range(1, towerHeight);
// steps: [ 1, 2, 3 ]
And now, we want to convert each step into the string we need it to be. Luckily, we already defined this as the GenerateStep
method:
var tower = steps.Select(step => GenerateStep(towerHeight, step));
// tower: [ " * ", " *** ", "*****" ]
And that's all you need to do to get the tower you're looking for. Of course, we still need to look at how you generate the step string, but I want you to see how simple the rest of the logic really is.
Now, all we need to do is figure out how to generate a given step, based on the tower height and current step number. We don't need to worry about loops or towers anymore, we can focus solely on generating a single step. This will be wrapped in the method we already defined:
public string GenerateStep(int towerHeight, int currentStep)
{
// ...
}
A step is always of the form:
[X amount of blanks][Y amount of asterisks][X amount of blanks]
So we really just need to figure out what X (which I'll call the padding) and Y (which I'll call the width of the tower) are.
We actually already established how to find the padding (X):
amount_of_blanks_on_one_side = tower_height - current_step
Or, in code:
int paddingSize = towerHeight - currentStep;
The width of the tower is slightly more complex. Let's look at some numbers to see if we spot the formula
STEP | WIDTH
------------
1 | 1
2 | 3
3 | 5
4 | 7
5 | 9
Depending on how you look at it, you should quickly figure out that the width is always one less than the double of the step number, i.e.:
int towerWidth = 2 * currentStep - 1;
So now we know our width and padding. The only thing that's left is how to generate a string of a given length with a repeating character. There is actually a string
constructor for that:
var myString = new string(myCharacter, amountOfCharacters);
Therefore, we can generate the appropriate strings:
string padding = new string(' ', paddingSize);
string towerPart = new string('*', towerWidth);
And then we put these string together:
string result = $"{padding}{towerPart}{padding}";
And that's it.
Putting it all together:
public IEnumerable<string> GenerateTower(int towerHeight)
{
var steps = Enumerable.Range(1, towerHeight);
var tower = steps.Select(step => GenerateStep(towerHeight, step));
return tower;
}
public string GenerateStep(int towerHeight, int currentStep)
{
int paddingSize = towerHeight - currentStep;
int towerWidth = 2 * currentStep - 1;
string padding = new string(' ', paddingSize);
string towerPart = new string('*', towerWidth);
return $"{padding}{towerPart}{padding}";
}
If you examine the steps closely, this actually mirrors the LINQ example you provided. But the LINQ example is horribly unreadable, which is why I opted for a bit more verbosity to ensure that the code remains clean and readable.
Could you optimize this further? Yes, and I'm sure this has already been golfed. But further condensing the code is only going to detract from the readability, which is why you're better off not optimizing any further (unless you are dealing with very specific performance constraints).
midpoint-row
vsmidpoint + row
. \$\endgroup\$