Why do I get 6 entries in an array created from a map with only 3 entities? - go

I have a seemingly simple issue with my understanding of map types in Go. If I create a simple map such as
var thisMap = map[string]string {
"8f14e45fceea167a5a36dedd4bea2543": "Charming",
"1679091c5a880faf6fb5e6087eb1b2dc": "Citi",
"e4da3b7fbbce2345d7772b0674a318d5": "Chase",
}
Populate the keys into an array
keys := make([]string, len(supportedCards))
for k := range supportedCards {
keys = append(keys, k)
}
Then try to join these keys into a comma separated value that I can append to any string
fmt.Println(strings.Join(keys,","))
I expect the result to be
8f14e45fceea167a5a36dedd4bea2543,1679091c5a880faf6fb5e6087eb1b2dc,e4da3b7fbbce2345d7772b0674a318d5
But what I really see is
,,,8f14e45fceea167a5a36dedd4bea2543,1679091c5a880faf6fb5e6087eb1b2dc,e4da3b7fbbce2345d7772b0674a318d5
Why does iterating through the map create 6 entries instead of just 3?
https://play.golang.org/p/Ou67K1Kfvsf

With
keys := make([]string, len(supportedCards))
you create a []string with three empty elements. You can assign them using their indicies.
When you append it later, new entries are added at the end, producing your result with a length of 6, where the first three are empty.

In addition to #xarantolus's answer, instead of using indices you can keep your for-range loop unchanged by:
keys := make([]string, 0, len(supportedCards))
The third argument is capacity of the slide. Ref: https://tour.golang.org/moretypes/13

Related

How to make array of objects that contains key-values in golang?

Let's say, I am iterating some data in go for loop.
for _, job := range orderJobs {}
for each iteration, I want a new object to be added in array and that object should contain the key value pairs.
So the final output should be something like
[
{
"order_id":"123"
"job_name":"JOB1"
}
{
"order_id":"456"
"job_name":"JOB2"
}
]
Should I declare and use go maps in this case ? If yes then how exactly I should declare ?
I tried declaring
Jobs := make(map[string]interface{})
and inserting key value pairs like below inside loop iteration
Jobs["order_id"] = "123"
it's not serving the purpose of creating array of objects.
Declare jobs as a slice:
var jobs []map[string]any
Append values to the slice in the for loop:
jobs = append(jobs, map[string]any{"order_id": "123", "job_name":"JOB1"})

Is there a way to delete first element from map?

Can I delete the first element in map? It is possible with slices slice = append(slice, slice[1:]...), but can I do something like this with maps?
Maps being hashtables don't have a specified order, so there's no way to delete keys in a defined order, unless you track keys in a separate slice, in the order you're adding them, something like:
type orderedMap struct {
data map[string]int
keys []string
mu *sync.RWMutex
}
func (o *orderedMap) Shift() (int, error) {
o.mu.Lock()
defer o.mu.Unlock()
if len(o.keys) == 0 {
return 0, ErrMapEmpty
}
i := o.data[o.keys[0]]
delete(o.data, o.keys[0])
o.keys = o.keys[1:]
return i, nil
}
Just to be unequivocal about why you can't really delete the "first" element from a map, let me reference the spec:
A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. The value of an uninitialized map is nil.
Added the emphasis on the fact that map items are unordered
Using a slice to preserve some notion of the order of keys is, fundamentally, flawed, though. Given operations like this:
foo := map[string]int{
"foo": 1,
"bar": 2,
}
// a bit later:
foo["foo"] = 3
Is the index/key foo now updated, or reassigned? Should it be treated as a new entry, appended to the slice if keys, or is it an in-place update? Things get muddled really quickly. The simple fact of the matter is that the map type doesn't contain an "order" of things, trying to make it have an order quickly devolves in a labour intensive task where you'll end up writing your own type.
As I said earlier: it's a hashtable. Elements within get reshuffled behind the scenes if the hashing algorithm used for the keys produces collisions, for example. This question has the feel of an X-Y problem: why do you need the values in the map to be ordered? Maybe a map simply isn't the right approach for your particular problem.

What is the best way to get substring info from a string

I am actually doing a function to get some data. The data has info that it's not necessary for me, so I'am searching for an optimal way to find it.
What I have is this
e := strings.Index(result, "Country") //Searching for the country info like Country: US
When I get the index I search for the Info that I need something like the next word. That's what I'm looking for.
Any idea to resolve this in an optimal way?
You can use FindStringIndex from the regexp package. It is guaranteed to run in time linear in the size of the input. FindStringIndex returns a slice of two elements defining the location of the matched string so you can easily get the next word.
I'm not sure what your result string looks like but if you can turn it into a format such as "City:San Fran;State:CA;Country:US" then this can easily be converted into a map. For instance:
result := "City:San Fran,State:CA,Country:US"
fields := strings.Split(result, ",")
fieldsMap := map[string]string{}
for _, field := range fields {
pair := strings.Split(field, ":")
fieldsMap[pair[0]] = pair[1]
}
Now you can access any value very easily:
country := fieldsMap["Country"] will give "US" if you fmt.Println(country)
It is another way to find the index of the string.
Split by that string and then length of the first index is the index of the first occurrence of the string.
https://play.golang.org/p/x7QWyuC_1wK
result := "City:San Fran,State:CA,Country:US"
// split the string
s := strings.Split(result, "US")
// index of US
index := len(s[0])
fmt.Println(index)
// Output
U 31

How to iterate through the list in aerospike using golang?

I am using the list data type (http://www.aerospike.com/docs/guide/cdt-list.html
) in aerospike using a golang client. I can use the ListInsertOp (https://godoc.org/github.com/aerospike/aerospike-client-go#ListInsertOp) to insert values in the list of a given entry.
However, I want to update/delete a given list value using the ListSetOp (https://godoc.org/github.com/aerospike/aerospike-client-go#ListSetOp) or the ListRemoveOp (https://godoc.org/github.com/aerospike/aerospike-client-go#ListRemoveOp)
In order to do this, I need an index. How can I get this index ? Is there a way I can iterate through all the list values and get the index and then perform the update or delete operation ?
Assuming that you have the list called List.
Let us say you want to replace element called value with newItem
You can do that like:
...
for index, item := range List {
if item == value {
List[index] = newItem
}
}
...
In the above snippet, index is the index at which element item is present. By this way you can also replace element present on the particular index in the list with value.
Sample example in playground: https://play.golang.org/p/qOmsY9fbL2
As usual, items in the list are indexed by their integer position starting from zero.
Aerospike also supports negative indexing to start backwards from end of list.
Lists documentation in the Aerospike:
Elements are ordered by integer position.
Lists documentation in the Aerospike's Node.js client:
List operations support negative indexing. If the index is negative, the resolved index starts backwards from end of list.
Index/Range examples:
Index 0: First item in list.
Index 4: Fifth item in list.
Index -1: Last item in list.
Index -3: Third to last item in list.
Also mentioned in Go client source.
list := []string{“a”, “b”, “c”, “d”, “e”}
bin := aerospike.NewBin("listbin", list)
err := client.PutBins(nil, key, bin)
// to replace “d”:
op := aerospike.ListSetOp(bin.Name, 3, "replaced")
_, err = client.Operate(nil, key, op)

Pattern for lookup in Go array

Go has convenient syntax to define array lookup tables:
var myTable = [...]string{
'a': "aaaa",
'b': "bbbb",
'z': "zoro",
}
In some cases (where keys are in known and not too big range) this is more efficient way to make table, than map. However, it is easy to make lookup in map and find if key not in it. But to do lookup by index in this array I have to do:
if index < len(myTable) {
if val := myTable[index]; val != "" {
// here I know index exists in array and val is its value
}
}
Is there simpler / more common pattern or library function to do this?
I don't think there is any special builtin syntax to remove the need for a bounds check here. One option would be to wrap the code in a custom type. For example:
type StringTable []string
func (st StringTable) Get(i int) string {
if i < 0 || i >= len(st) {
return ""
}
return st[i]
}
You can still use the same initialiser syntax with the custom type:
myTable := StringTable{
'a': "aaaa",
'b': "bbbb",
'z': "zoro",
}
fmt.Printf("%#v\n", myTable.Get('a'))
fmt.Printf("%#v\n", myTable.Get(-5))
fmt.Printf("%#v\n", myTable.Get('~')) // greater than 'z'
You can play around with this example here: http://play.golang.org/p/nhti2dVE8B
In some cases (where keys are in known and not too big range) this is more efficient way to make table, than map
Yes. You want to translate your key into an offset in the array. Then you can do the lookup in constant time.
Let's say you know all your keys will be in the set A-Z. So you create an array of 26 entries. When a key comes in, you subtract the ASCII value of "A" to get the index into your table. Boom, constant-time lookups, which will be much faster than a map lookup.
Note that you don't actually store the key anywhere, it's implicit. If you want to print out your table, you'd need to generate all keys (A-Z) yourself.

Resources