How to output iterate over first N elements of slice? - go

I need to take applicant's first name, second name and GPA, then output only the first N applicants. For example, I have 5 applicants, but only N=3 can pass through.
To do this task, I decided to use a slice of struct.
The struct looks like this:
type Applicant struct {
firstName string
secondName string
GPA float64
}
I created a slice and initialized it:
applicants := []Applicant{}
...
fmt.Scan(&firstName, &lastName, &GPA)
applicants = append(applicants, Applicant{firstName, lastName, GPA})
Now my task is to output only names of first 3 applicants with highest GPA. I already sorted the slice from the best GPA to the worst.
I tried to do output applicants slice like this, but got error:
for _, applicant := range applicants {
fmt.Println(applicant.secondName + " " + applicant.secondName)
}
Can you help me with slice name output?

To get the first 3 with highest GPA you first sort the slice (what you alread did) and then just create a subslice:
func GetTopThree(applicants []Applicant) []Applicant {
sort.Slice(applicants, func(i, j int) bool {
return applicants[i].GPA > applicants[j].GPA
})
return applicants[:3]
}
To just get the names you can create a new slice
func GetTopThreeNames(applicants []Applicant) []string {
var topThree []string
for i := 0; i < int(math.Min(3, float64(len(applicants)))); i++ {
topThree = append(topThree, applicants[i].firstName)
}
return topThree
}

If you want to map the first names and last names separately, this could be an approach:
func TopThreeNames(applicants []Applicant) [][2]string {
top := applicants[:int(math.Min(3, float64(len(applicants))))]
var names [][2]string
for _, a := range top {
names = append(names, [2]string{a.firstName, a.secondName})
}
return names
}
The function maps each Applicant element to an array of length two, whereby the first element is equal to its first name and the second element to its second name.
For instance (unsafe since the length of the slice could be empty):
names := TopThreeNames(applicants)
first := names[0]
fmt.Printf("First name: %s and last name: %s\n", first[0], first[1])

If your task really is just to print out the names then this is one possible way
for i := 0; i < 3 && i < len(applicants); i++ {
fmt.Printf("%s %s\n", applicants[i].firstName, applicants[i].secondName)
}
Note that the list must be sorted first like is is shown in other posts.

Related

Iterating slice struct within struct using reflection

I'm trying to achieve the following:
Use-case:
I have three structures, I need to compare 2 of those against one. (in the example described as: a & b need to be compared against full)
Reflection is used to loop over every field, retrieve the name of the field. And comparing the difference between a & full, b & full, storing the results in a shared structure.
If the field equals World, we know it's a slice struct:
I need to retrieve the first index of the Bar slice within the Foo structure.
Even though the variable is a slice, I know it will always have a length of 1 in this use-case.
When retrieved I need to loop over those fields, like what is happening in the previous if statement.
Example code:
type Foo struct {
Hello string
World []Bar
}
type Bar struct {
Fish string
}
type Result struct {
Field string
Correct_A bool
Distance_A int
Correct_B bool
Distance_B int
Result []Result
}
func compare_structs() {
var full, a, b Foo
// filling in all variables...
result := []Result{}
rfx_f := reflect.ValueOf(full)
rfx_a := reflect.ValueOf(a)
rfx_b := reflect.ValueOf(b)
type_result := rfx_f.Type()
for i := 0; i < rfx_f.NumField(); i++ {
tmp_res := Result{
Field: type_result.Field(i).Name,
}
if reflect.TypeOf(full).Field(i).Type.Kind() != reflect.Slice {
value := rfx_f.Field(i).Interface()
value_a := rfx_a.FieldByName(tmp_res.Field).Interface()
value_b := rfx_b.FieldByName(tmp_res.Field).Interface()
// functions to compare the values of this field
tmp_res.compare(value, value_a, value_b)
tmp_res.lev(value, value_a, value_b)
result = append(result, tmp_res)
} else if tmp_res.Field == "World" {
/*
I need to retrieve the first index of the Bar slice within the Foo structure.
Even though the variable is a slice, I know it will always have a length of 1 in this use-case.
When retrieved I need to loop over those fields, like what is happening in the previous if statement.
*/
}
}
}
You first need to get the field:
wordField:=rfx_f.Field(i)
which you know to be a slice, so you index it to get the first element
item:=wordField.Index(0)
This will panic if index is out of range.
Then you can iterate the fields:
for fieldIx:=0;fieldIx<item.NumField();fieldIx++ {
field:=item.Field(fieldIx)
}

How to get the index of a string in an array?

Here is my sample code:
slice_of_string := strings.Split("root/alpha/belta", "/")
res1 := bytes.IndexAny(slice_of_string , "alpha")
I got this error
./prog.go:16:24: cannot use a (type []string) as type []byte in argument to bytes.IndexAny
The logic here is when I input a path and a folder name (or file name), I want to know the level of the folder name (or a file name) in that path.
I do it by:
Split the path to an array
Get the index of the folder name (or a file name) in the path
If the index is 0 then the level would be 1, etc.
You probably need to loop over the slice and find the element that you are looking for.
func main() {
path := "root/alpha/belta"
key := "alpha"
index := getIndexInPath(path, key)
fmt.Println(index)
}
func getIndexInPath(path string, key string) int {
parts := strings.Split(path, "/")
if len(parts) > 0 {
for i := len(parts) - 1; i >= 0; i-- {
if parts[i] == key {
return i
}
}
}
return -1
}
Note that the loop is backwards to address the logic issue that Burak Serdar pointed out that it may fail on a path like /a/a/a/a otherwise.
Use strings.IndexAny instead of bytes.IndexAny if you want to operate on a []string.
there are no inbuild function available in standard library to search in slice of string, but if slice of string is sorted then you can use sort.SearchStrings to search. But in case of unsorted slice of string you have to implement it using for loop.

How to loop through UUID items

How do I loop through a slice composed of UUIDS? My values comes from db via rows.Next()
Here's how I'm appending my uuid values to my slice (really don't know if its proper)
type Images struct {
image_id uuid.UUID `gorm:"type:uuid;primary_key;"`
}
var new_images []Images
for olds.Next() {
olds.Scan(&oldimages.image_id)
new_images = append(new_images , Images{image_id: oldimages.image_id})
}
olds here is the rows im getting from gorm Rows
olds, err := db.Raw("SELECT images_received.image_id FROM old_pics").Rows()
defer olds.Close()
Heres the function in looping I was given but its for int i dont know how to use this for uuid:
func islice(s []int, n int, f func([]int)) {
for i := 0; i < len(s); i += n {
var section []int
if i > len(s)-n {
section = s[i:]
} else {
section = s[i : i+n]
}
f(section)
}
}
Any idea how I do this? Currently for uuid im using the "github.com/satori/go.uuid" lib
I got the function from another SO question, My goal is to iterate over the rows, but rows.Next() doesnt allow that I guess in order to do that I thought I needed to append them into a slice, so I can get them by fours.
Hence leading to this question.
All you need to do is replace []int with []uuid.UUID everywhere in your islice function, including the parameter types. The functionality of islice() is not bound to []int if thats what your problem is.

DeDuplicate Array of Structs

I have an array of struct and I want to remove all the duplicates element but keep the last element in the array. Something similar to hashmap where I can update the last struct matched every time to the new array
I have a struct something like this
type samplestruct struct {
value1 string
value2 string
value3 string
value4 string
value5 string
}
In my array of struct if value1, value2 and value3 of any struct is same , remove all the duplicates and keep the last struct.
func unique(sample []samplestruct) []samplestruct {
var unique []samplestruct
for _, v := range sample {
skip := false
for _, u := range unique {
if v.value1 == u.value1 && v.value2 == u.value2 && v.value3 == u.value3 {
skip = true
break
}
}
if !skip {
unique = append(unique, v)
}
}
return unique
}
This code return me the first struct that matched the condition provided but I want the last struct that matches the condition
Given Input -
[
samplestruct{"ram","rahim","india","34","india"},
samplestruct{"ram","rahim","india","38","America"},
samplestruct{"ram","rahim","india","40","Jamica"},
samplestruct{"amit","rawat","bangladesh","35","hawai"},
samplestruct{"amit","rawat","bangladesh","36","india"}
]
ExpectedOutput -
[
samplestruct{"ram","rahim","india","40","Jamica"},
samplestruct{"amit","rawat","bangladesh","36","india"}
]
The code in the question is almost there. When a matching element is found in unique, overwrite the element with the current value:
func unique(sample []samplestruct) []samplestruct {
var unique []samplestruct
sampleLoop:
for _, v := range sample {
for i, u := range unique {
if v.value1 == u.value1 && v.value2 == u.value2 && v.value3 == u.value3 {
unique[i] = v
continue sampleLoop
}
}
unique = append(unique, v)
}
return unique
}
The map-based approaches shown in other answers may be more appropriate depending on the size of the data set and number of surviving elements. Here's a correct implementation of the map approach:
func unique(sample []samplestruct) []samplestruct {
var unique []samplestruct
type key struct{ value1, value2, value3 string }
m := make(map[key]int)
for _, v := range sample {
k := key{v.value1, v.value2, v.value3}
if i, ok := m[k]; ok {
// Overwrite previous value per requirement in
// question to keep last matching value.
unique[i] = v
} else {
// Unique key found. Record position and collect
// in result.
m[k] = len(unique)
unique = append(unique, v)
}
}
return unique
}
Probably you should use a map here, use the important values as the key, when you encounter a duplicate and check for the key, you replace the value in the map.
Currently you are adding the values to the unique array if you haven't encountered them before, and then if you encounter one in the array after, you skip it. This is why you are only adding the first encounter of each struct which is the opposite of what you want.
You could either produce the key to the map as a concatenation of your important values (1 to 3), or use a struct of the three values as a key, and build the new key struct for each items and then search for it in the map.
Using a map will also be more performant than an array, as you can lookup much quicker in a map than iterating the unique array each time.
Nice little exercise, here is one solution which I will explain below:
package main
import "fmt"
func main() {
all := []person{
{"ram", "rahim", "india", "34", "india"},
{"ram", "rahim", "india", "38", "America"},
{"ram", "rahim", "india", "40", "Jamica"},
{"amit", "rawat", "bangladesh", "35", "hawai"},
{"amit", "rawat", "bangladesh", "36", "india"},
}
var deduped []person
// add the last occurrence always
for i := len(all) - 1; i >= 0; i-- {
if !contains(deduped, all[i]) {
// "append" to the front of the array
deduped = append([]person{all[i]}, deduped...)
}
}
for _, x := range deduped {
fmt.Println(x)
}
}
type person [5]string
func eq(a, b person) bool {
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2]
}
func contains(list []person, x person) bool {
for i := range list {
if eq(x, list[i]) {
return true
}
}
return false
}
We step through the input array backwards in order to catch the last of multiple equal items. Then we want to append that item to the back of the deduped array. That is why we revert the append operation, creating a new temporary one-item person slice and append the previous to it.
Efficiency-wise, this solution has some drawbacks, appending to the one-item slice will use O(n²) space as it produces a new slice every time, pre-allocating an array of len(all), appending to it, and reversing it afterwards would solve that problem.
The second performance issue that might arise if you do this for a zillion persons is the contains function which is O(n²) lookups for the program. This could be solved with a map[person]bool.
Use a map. First scan the list and set up a map with the first 3 values as the key for the map. The map value for each key will be the last found
Then walk the map it will be set to the correct values
package main
import (
"fmt"
"strings"
)
type samplestruct struct {
value1 string
value2 string
value3 string
value4 string
value5 string
}
func mkey(x samplestruct) string {
return strings.Join([]string{x.value1, x.value2, x.value3}, "-")
}
func main() {
cm := make(map[string]samplestruct)
exampledata := []samplestruct{samplestruct{"ram", "rahim", "india", "34", "india"},
samplestruct{"ram", "rahim", "india", "38", "America"},
samplestruct{"ram", "rahim", "india", "40", "Jamica"},
samplestruct{"amit", "rawat", "bangladesh", "35", "hawai"},
samplestruct{"amit", "rawat", "bangladesh", "36", "india"}}
for _, x := range exampledata {
k := mkey(x)
cm[k] = x
}
for x := range cm {
fmt.Println(cm[x])
}
}
https://play.golang.org/p/ITD0VjhFQEk

Go weird behaviour - variable not incrementing correctly

I have the following code that adds a new element to a slice if it doesnt exist already. If it does exist then the qty property should be incremented of the existing element instead of a new element being added:
package main
import (
"fmt"
)
type BoxItem struct {
Id int
Qty int
}
type Box struct {
BoxItems []BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// If the item exists already then increment its qty
for _, item := range box.BoxItems {
if item.Id == boxItem.Id {
item.Qty++
return item
}
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
func main() {
boxItems := []BoxItem{}
box := Box{boxItems}
boxItem := BoxItem{Id: 1, Qty: 1}
// Add this item 3 times its qty should be increased to 3 afterwards
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
fmt.Println(len(box.BoxItems)) // Prints 1 which is correct
for _, item := range box.BoxItems {
fmt.Println(item.Qty) // Prints 1 when it should print 3
}
}
The problem is that the qty is never incremented correctly. It always ends in 1 when it should be 3 in the example provided.
I have debugged the code and it does appear that the increment section is reached but the value just isnt persisted to the item.
What is wrong here?
You are incrementing Qty in the copy of the box.BoxItems because range will yield the copy of the elements in the slice. See this example.
So, in for _, item := range box.BoxItems, item is a copy of of the elements in box.BoxItems.
Change your loop to
for i := 0; i < len(box.BoxItems); i++ {
if box.boxItems[i].Id == boxItem.Id {
box.boxItems[i].Qty++
return box.BoxItems[i]
}
}
Playground
I will answer your question pretty much like others have done. However, not that the problem you try to solve is not best served by looping over a range of values. Read on:
Solution to your question
Like others have said, for-range provide an immutable iteration over the range of values. That means any change you make to the value provided in the iteration will be lost. It's basically giving you a copy of the real value, not the actual value.
for _, item := range box.BoxItems {
// ^-not the real `item`, it's a copy!
A way around this is to keep track of the indexing value in the for idx, val := range, and use this idx to address the value you look for directly.
If you change your for-loop to keep the index value:
for i, item := range box.BoxItems {
// ^-keep this
You will be able to reference the actual item in the array you loop on:
for i, item := range box.BoxItems {
// Here, item is a copy of the value at box.BoxItems[i]
if item.Id == boxItem.Id {
// Refer directly to an item inside the slice
box.BoxItems[i].Qty++
return box.BoxItems[i] // Need to return the actual one, not the copy
}
}
Playground
I would favor this approach over the for i; i<Len; i++ one as I find it more readable. But this is simply a matter of taste and the for i form will be more efficient (beware of premature-optimization!).
Your real problem is
What you're trying to do is to avoid duplicating BoxItems if their Id already exists. To do this, you iterate over the whole range of the box.BoxItems slice. If you have N items in your box.BoxItems slice, you will potentially iterate over all N items before finding out that the item you're looking for doesn't exist! Basically, this means your algorithm is O(N).
If you increment Id in natural order
That is, 0, 1, 2, 3, ..., n - 1, n, you can keep using a slice to index your box items. You would do like this:
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// Lookup your item by Id
if boxItem.Id < len(box.BoxItems) {
// It exists, do don't create it, just increment
item := box.BoxItems[boxItem.Id]
item.Qty++
box.BoxItems[boxItem.Id] = item
return item
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
Playground
If you increment Id in any order
You should use a datastructure that offers fast lookups, such as the built-in map, which offers O(1) lookups (that means, you need to do a single operation to find your item, not n operations).
type Box struct {
BoxItems map[int]BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// Lookup the map by Id
item, ok := box.BoxItems[boxItem.Id]
if ok {
// It exists, do don't create it, just increment
item.Qty++
} else {
item = boxItem
}
// New item so add it to the map
box.BoxItems[boxItem.Id] = item
return item
}
Playground
This is a more correct way to solve your problem.
In the index, value := range someSlice, the value is a fresh new copy of someSlice[index].
package main
import (
"fmt"
)
type BoxItem struct {
Id int
Qty int
}
type Box struct {
BoxItems []BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// If the item exists already then increment its qty
for i := range box.BoxItems {
item := &box.BoxItems[i]
if item.Id == boxItem.Id {
item.Qty++
return *item
}
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
func main() {
boxItems := []BoxItem{}
box := Box{boxItems}
boxItem := BoxItem{Id: 1, Qty: 1}
// Add this item 3 times its qty should be increased to 3 afterwards
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
fmt.Println(len(box.BoxItems)) // Prints 1 which is correct
for _, item := range box.BoxItems {
fmt.Println(item.Qty) // Prints 1 when it should print 3
}
}
Playground
Output:
1
3

Resources