Programming With Go

Keep Longest And Shortest Word

Write a function that accepts a pointer to a 2-dimensional slice of string slices. Your function should modify this slice in-place so, each nested slice only contains their longest and shortest word. In other words, remove all elements from each nested slice that is not the longest or shortest word contained within it. Your function should not return a value, it should simply modify the input slice.

You may assume each nested slice will only contain words with unique lengths (i.e., no two words have the same length). If a slice only contains one word, that word is considered both the longest and the shortest word and should remain in the slice. If a slice contains no words, then no change is necessary.

Note: The remaining words should maintain the order in which they appear in the original slice.

Sample Input

words = &[][]string{ {"best", "course", "yes"}, {"hello"}, {"a", "ab", "abc", "abcd"}, {} }

Sample Output

[[course yes] [hello] [a abcd] []]// These are the values of the slices after the function has ran, not the return value

Solution

package main

func keepLongestAndShortestWordHelper(words []string) []string {
	if len(words) == 0 {
		return words
	}

	longest := words[0]
	shortest := words[0]
	longestIdx := 0
	shortestIdx := 0

	for idx, word := range words {
		if len(word) > len(longest) {
			longest = word
			longestIdx = idx
		}
		if len(word) < len(shortest) {
			shortest = word
			shortestIdx = idx
		}
	}

	if longest == shortest {
		return []string{longest}
	} else if longestIdx > shortestIdx {
		return []string{shortest, longest}
	} else {
		return []string{longest, shortest}
	}
}

func KeepLongestAndShortestWord(wordSlices *[][]string) {
	for wordsIdx, words := range *wordSlices {
		newWords := keepLongestAndShortestWordHelper(words)
		(*wordSlices)[wordsIdx] = newWords
	}
}

Library Books

Write the following structs and their corresponding methods:

Book: a struct that has four fields: an int id, string title, string author, and int quantity.

Library: a struct that one field named books which is a slice of pointer to the Book struct. Library should also contain the following two methods.

  • CheckoutBook(id int): a method that returns a pointer to a Book whose id field matches the id parameter, as well as a bool indicating whether the book can be checked out. A book can only be checked out if it has at least one copy remaining and exists in the library. If the book exists and has at least one copy you should first decrement it’s number of copies by one and then return the pointer to it along with the bool true. If the book does not exist or has insufficient copies the method should return nil and false.
  • ReturnBook(id int): a method that returns a boolean indicating if the book corresponding with the id parameter can be returned. A book can only be returned if it exists in the library, if it does not your method should return false. If the book does exist, you should increment it’s number of copies before returning true.

For all test cases you may assume each book has a unique id.

Please note that a book that exists in the library can be returned even if it hasn’t been checked out.

Solution 1

package main

type Book struct {
	id     int
	title  string
	author string
	copies int
}
type Library struct {
	books []*Book
}

func (l *Library) findBook(id int) *Book {
	for _, book := range l.books {
		if book.id == id {
			return book
		}
	}
	return nil
}

func (l *Library) CheckoutBook(id int) (*Book, bool) {
	book := l.findBook(id)

	if book == nil || book.copies < 1 {
		return nil, false
	}

	book.copies -= 1
	return book, true
}

func (l *Library) ReturnBook(id int) bool {
	book := l.findBook(id)

	if book != nil {
		book.copies += 1
		return true
	}

	return false
}

Solution 2

package main

type Book struct {
	id     int
	title  string
	author string
	copies int
}

type Library struct {
	books []*Book
}

func (l *Library) CheckoutBook(id int) (*Book, bool) {
	for _, book := range l.books {
		if book.id == id && book.copies >= 1 {
			book.copies -= 1
			return book, true
		}
	}

	return nil, false
}

func (l *Library) ReturnBook(id int) bool {
	for _, book := range l.books {
		if book.id == id {
			book.copies += 1
			return true
		}
	}
	return false
}

Magic Squares
Write a function that determines if a two-dimensional slice of distinct positive integers representing a magic square. In this context, a magic square is a square slice where all numbers aligned horizontally, vertically and diagonally sum to the same value.

Take this square (2-dimensional) slice as an example:

  -------------
  | 2 | 7 | 6 |  15
  -------------
  | 9 | 5 | 1 |  15
  -------------
  | 4 | 3 | 8 |  15
  ------------- 
15  15  15  15  15 

As you can see each horizontal line, vertical line, and diagonal line all sum to the same value (the extra two 15’s in the bottom are for the two diagonals).

You may assume that the given square slice will always contain distinct, positive integers (excluding zeros) and that the minimum size of the square will be three (i.e., a width of three and height of three). You may also assume the dimensions of the slice will always be the same (i.e., it will be square).

Note: Make sure to consider the case where the size of the square is greater than three. In this case you do NOT need to check more than two diagonals.

Sample Input #1

square = [][]int{ {5, 6, 19, 68}, {69, 18, 3, 8}, {4, 7, 70, 17}, {20, 67, 6, 5} }

Sample Output #1

true // all lines sum to 98

Solution

package main

func sumSlice(nums []int) (sum int) {
	for _, num := range nums {
		sum += num
	}
	return
}

func DetectMagicSquare(square [][]int) bool {
	desiredSum := sumSlice(square[0])
	columnSums := make([]int, len(square))
	diagonalLeftRightSum := 0
	diagonalRightLeftSum := 0

	for row, rowNums := range square {
		rowSum := 0

		for col, num := range rowNums {
			rowSum += num
			columnSums[col] += num

			if row == col {
				diagonalLeftRightSum += num
			}
			if col == len(square)-1-row {
				diagonalRightLeftSum += num
			}

			if row == len(square)-1 && columnSums[col] != desiredSum {
				return false
			}
		}

		if rowSum != desiredSum {
			return false
		}
	}
	return diagonalRightLeftSum == desiredSum && diagonalLeftRightSum == desiredSum

Multiply Strings Concurrently

Write a function that concurrently multiplies strings by a given factor. This function needs to accept a slice of strings and a positive (possibly zero), integer factor and return a new slice containing the resulting multiplied strings.

The multiplication of a string is simply the repeated concatenation of itself. For example, multiplying “tim” by 3 results in “timtimtim”.

The slice that is returned must contain the multiplied strings in the order in which they appeared in the original input slice. This function must also perform each string multiplication concurrently, using a go routine.

Note: Multiplying a string by zero results in an empty string: “”.

Sample Input

strings = []string{"bird", "plane", "superman"} factor = 3

Sample Output

[birdbirdbird planeplaneplane supermansupermansuperman]

Solution

package main

type MultiplyStringResult struct {
	str string
	idx int
}

func multiplyString(str string, factor uint, idx int, result chan<- MultiplyStringResult) {
	newString := ""

	for i := uint(0); i < factor; i++ {
		newString = newString + str
	}

	resultStruct := MultiplyStringResult{newString, idx}
	result <- resultStruct
}

func MultiplyStringsConcurrently(strings []string, factor uint) []string {
	multipliedStrings := make([]string, len(strings))
	resultsChannel := make(chan MultiplyStringResult)

	for idx, str := range strings {
		go multiplyString(str, factor, idx, resultsChannel)
	}

	resultsGathered := 0
	for resultsGathered < len(strings) {
		multipliedString := <-resultsChannel
		resultsGathered++
		multipliedStrings[multipliedString.idx] = multipliedString.str
	}

	return multipliedStrings
}

Item Interface

Write the following interface, structs and methods as defined below.

  • Item: an interface that defines a single method:
    • GetPrice() float64: a method that returns a float representing the price of an item.
  • Gravel: a struct that implements the Item interface and has the following fields:
    • lbs: a float64 value representing the quantity of gravel.
    • pricePerLb: a float64 value representing the price per lb of gravel.
    • grade: a string value representing the grade of the gravel. The possible values for this are “fine”, “medium” and “coarse”.
  • Shovel: a struct that implements the Item interface and has the following fields:
    • size: a string value representing the size of the shovel. The possible values are “S”, “M” and “L”.
    • price: the price of a shovel of the specified size.

After writing the types and methods specified above complete the CalculateOrderPrice function. This function accepts a slice of Item’s (representing a users order) and returns the total price of the order (i.e., the sum of the price of all the items).

Solution

package main

type Item interface {
	GetPrice() float64
}

type Gravel struct {
	lbs        float64
	pricePerLb float64
	grade      string
}

type Shovel struct {
	size  string
	price float64
}

func (g *Gravel) GetPrice() float64 {
	return g.lbs * g.pricePerLb
}

func (s *Shovel) GetPrice() float64 {
	return s.price
}

func CalculateOrderPrice(order []Item) float64 {
	totalPrice := 0.0

	for _, item := range order {
		totalPrice += item.GetPrice()
	}

	return totalPrice
}