diff --git a/api_logs/api-logs.go b/api_logs/api-logs.go
index e41caecfd937d175a8f7b0a0cc9335ce3760f8de..bfd4ad1f3eb87f61e0b5e90e13dd0ceba0a92927 100644
--- a/api_logs/api-logs.go
+++ b/api_logs/api-logs.go
@@ -2,7 +2,7 @@ package api_logs
 
 import (
 	"github.com/thoas/go-funk"
-	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/string_utils"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/number_utils"
 	"net/url"
 	"strings"
 	"time"
@@ -41,7 +41,7 @@ func GenerateIncomingAPILog(startTime time.Time, requestID *string, claim map[st
 
 	if accountID == 0 {
 		if accountIDParam, ok := req.QueryStringParameters["account_id"]; ok {
-			if i64, err := string_utils.StringToInt64(accountIDParam); err == nil && i64 > 0 {
+			if i64, err := number_utils.StringToInt64(accountIDParam); err == nil && i64 > 0 {
 				accountID = i64
 			}
 		}
@@ -49,7 +49,7 @@ func GenerateIncomingAPILog(startTime time.Time, requestID *string, claim map[st
 
 	if providerID == 0 {
 		if providerIDParam, ok := req.QueryStringParameters["provider_id"]; ok {
-			if i64, err := string_utils.StringToInt64(providerIDParam); err == nil && i64 > 0 {
+			if i64, err := number_utils.StringToInt64(providerIDParam); err == nil && i64 > 0 {
 				providerID = i64
 			}
 		}
diff --git a/api_responses/api_responses.go b/api_responses/api_responses.go
index 4774cc3513f5ad0a641487362475a99afc653340..b728cb5f0ba6bac519729c12c1b5edde4006c800 100644
--- a/api_responses/api_responses.go
+++ b/api_responses/api_responses.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/map_utils"
 	"net/http"
 	"regexp"
 	"strconv"
@@ -46,14 +47,14 @@ func Error(err error, msg string, statusCode int) (events.APIGatewayProxyRespons
 	if err != nil {
 		return events.APIGatewayProxyResponse{
 			StatusCode: statusCode,
-			Headers:    utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
+			Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 			Body:       "{ \"error\": \"" + http.StatusText(http.StatusInternalServerError) + "\"}",
 		}, nil
 	}
 
 	return events.APIGatewayProxyResponse{
 		StatusCode: statusCode,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 		Body:       string(bodyBytes),
 	}, errors.New(msg)
 }
@@ -136,7 +137,7 @@ func DatabaseServerError(err error, msg string) (events.APIGatewayProxyResponse,
 	if marshalError != nil {
 		return events.APIGatewayProxyResponse{
 			StatusCode: statusCode,
-			Headers:    utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
+			Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 			Body:       "{ \"error\": \"" + http.StatusText(http.StatusInternalServerError) + "\"}",
 		}, nil
 	}
@@ -148,7 +149,7 @@ func DatabaseServerError(err error, msg string) (events.APIGatewayProxyResponse,
 
 	return events.APIGatewayProxyResponse{
 		StatusCode: statusCode,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 		Body:       string(bodyBytes),
 	}, err
 }
@@ -230,7 +231,7 @@ func ClientError(status int, message string) (events.APIGatewayProxyResponse, er
 
 	return events.APIGatewayProxyResponse{
 		StatusCode: status,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 		Body:       string(b),
 	}, errors.New(message)
 }
diff --git a/audit/audit.go b/audit/audit.go
index 4b27f0e229b9ab07ec4bbcaf6b91ce519a50d157..580513581cf6a07d8e1e2f08a9fdcff7fa8e6294 100644
--- a/audit/audit.go
+++ b/audit/audit.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/number_utils"
 	"reflect"
 	"regexp"
 	"strconv"
@@ -77,7 +78,7 @@ func GetChanges(original interface{}, new interface{}) (map[string]interface{},
 				continue
 			}
 
-			index, _ := string_utils.StringToInt64(indexString)
+			index, _ := number_utils.StringToInt64(indexString)
 			field := ToSnakeCase(change.Path[2])
 
 			if len(change.Path) == 5 && string_utils.IsNumericString(change.Path[3]) {
diff --git a/cognito/cognito.go b/cognito/cognito.go
index d12159192396585c0899b99663b5b8de1b75a574..ac79da6f825d272f7e2756c675a1ede4e2eeafae 100644
--- a/cognito/cognito.go
+++ b/cognito/cognito.go
@@ -107,8 +107,8 @@ func confirmForgotPassword(appClientID string, username string, password string,
 func confirmPasswordReset(appClientID string, username string, password string, initiateAuthOutput *cognitoidentityprovider.InitiateAuthOutput) (*cognitoidentityprovider.RespondToAuthChallengeOutput, error) {
 	// Respond to the Auth challenge to change the user's password
 	authChallengeParameters := map[string]*string{
-		"USERNAME":     utils.PointerValue(username),
-		"NEW_PASSWORD": utils.PointerValue(password),
+		"USERNAME":     utils.ValueToPointer(username),
+		"NEW_PASSWORD": utils.ValueToPointer(password),
 	}
 	respondToAuthChallengeInput := cognitoidentityprovider.RespondToAuthChallengeInput{
 		ChallengeName:      initiateAuthOutput.ChallengeName,
@@ -126,11 +126,11 @@ func confirmPasswordReset(appClientID string, username string, password string,
 func ConfirmPasswordReset(appClientID string, username string, password string, confirmationCode string) (interface{}, error) {
 	// Initiate an auth for the user to see if a password reset or
 	authParameters := map[string]*string{
-		"USERNAME": utils.PointerValue(username),
-		"PASSWORD": utils.PointerValue(confirmationCode),
+		"USERNAME": utils.ValueToPointer(username),
+		"PASSWORD": utils.ValueToPointer(confirmationCode),
 	}
 	initiateAuthInput := cognitoidentityprovider.InitiateAuthInput{
-		AuthFlow:       utils.PointerValue(cognitoidentityprovider.ExplicitAuthFlowsTypeUserPasswordAuth),
+		AuthFlow:       utils.ValueToPointer(cognitoidentityprovider.ExplicitAuthFlowsTypeUserPasswordAuth),
 		AuthParameters: authParameters,
 		ClientId:       &appClientID,
 	}
@@ -142,7 +142,7 @@ func ConfirmPasswordReset(appClientID string, username string, password string,
 		}
 		return nil, err
 	}
-	if utils.Unwrap(res.ChallengeName) == cognitoidentityprovider.ChallengeNameTypeNewPasswordRequired {
+	if utils.PointerToValue(res.ChallengeName) == cognitoidentityprovider.ChallengeNameTypeNewPasswordRequired {
 		return confirmPasswordReset(appClientID, username, password, res)
 	}
 
diff --git a/handler_utils/debug.go b/handler_utils/debug.go
index c6c8ea99c18c6d88e907a7afa3cf7cda5b3139b8..06817f60158d452ff4db373db3003365b913a2a7 100644
--- a/handler_utils/debug.go
+++ b/handler_utils/debug.go
@@ -98,7 +98,7 @@ func ServeSQSFunctions(ctx context.Context, lambdaHandler lambda.Handler, w http
 				Body: body,
 				MessageAttributes: map[string]events.SQSMessageAttribute{
 					"type": {
-						StringValue: utils.PointerValue(sqsType),
+						StringValue: utils.ValueToPointer(sqsType),
 					},
 				},
 			},
diff --git a/map_utils/map_utils.go b/map_utils/map_utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..5a41270d8bb5059d5572f5de954ad11190e28eef
--- /dev/null
+++ b/map_utils/map_utils.go
@@ -0,0 +1,53 @@
+package map_utils
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// MapStringInterfaceToMapStringString converts a generic value typed map to a map with string values
+func MapStringInterfaceToMapStringString(inputMap map[string]interface{}) map[string]string {
+	query := make(map[string]string)
+	for mapKey, mapVal := range inputMap {
+		// Check if mapVal is a slice or a single value
+		switch mapValTyped := mapVal.(type) {
+		case []interface{}:
+			// Slice - convert each element individually
+			var mapValString []string
+
+			// Loop through each element in the slice and check the type
+			for _, sliceElem := range mapValTyped {
+				switch sliceElemTyped := sliceElem.(type) {
+				case string:
+					// Enclose strings in escaped quotations
+					mapValString = append(mapValString, fmt.Sprintf("\"%v\"", sliceElemTyped))
+				case float64:
+					// Use FormatFloat for least amount of precision.
+					mapValString = append(mapValString, strconv.FormatFloat(sliceElemTyped, 'f', -1, 64))
+				default:
+					// Convert to string
+					mapValString = append(mapValString, fmt.Sprintf("%v", sliceElemTyped))
+				}
+			}
+			// Join as a comma seperated array
+			query[mapKey] = "[" + strings.Join(mapValString, ",") + "]"
+		default:
+			// Single value - convert to string
+			query[mapKey] = fmt.Sprintf("%v", mapVal)
+		}
+	}
+
+	return query
+}
+
+// MergeMaps If there are similar properties in the maps, the last one will be used as the value
+func MergeMaps(maps ...map[string]string) map[string]string {
+	ret := map[string]string{}
+	for _, mapV := range maps {
+		for k, v := range mapV {
+			ret[k] = v
+		}
+	}
+	return ret
+}
diff --git a/number_utils/number_utils.go b/number_utils/number_utils.go
index d183fe9fbcaef6b730943dcd276cf82039a486b2..9c4221b6bcec705627f3f27889a4f50b6c5ad29f 100644
--- a/number_utils/number_utils.go
+++ b/number_utils/number_utils.go
@@ -1,6 +1,9 @@
 package number_utils
 
-import "math"
+import (
+	"math"
+	"strconv"
+)
 
 func RoundFloat(value float64) float64 {
 	return math.Round(value*100) / 100 // 2 decimal places
@@ -11,16 +14,19 @@ func RoundFloatTo(value float64, to int) float64 {
 	return math.Round(value*toValue) / toValue
 }
 
-func UnwrapFloat64(f *float64) float64 {
-	if f == nil {
-		return 0.0
-	}
-	return *f
+func StringToInt(stringValue string) (int, error) {
+	return strconv.Atoi(stringValue)
+}
+
+func StringToInt64(stringValue string) (int64, error) {
+	number, err := strconv.ParseInt(stringValue, 10, 64)
+	return number, err
 }
 
-func UnwrapInt64(i *int64) int64 {
-	if i == nil {
-		return 0
+func StringToFloat64(stringValue string) (float64, error) {
+	number, err := strconv.ParseFloat(stringValue, 64)
+	if err != nil {
+		return 0, err
 	}
-	return *i
+	return number, nil
 }
diff --git a/responses/responses.go b/responses/responses.go
index 0cec3b118b4f7021ac4961ba1bd5581078cd85c8..78d1d9243c28eaf364f5cfa3152aab6317a0c536 100644
--- a/responses/responses.go
+++ b/responses/responses.go
@@ -2,6 +2,7 @@ package responses
 
 import (
 	"encoding/json"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/map_utils"
 	"net/http"
 
 	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/logs"
@@ -29,7 +30,7 @@ func NotFoundResponse(err error) (events.APIGatewayProxyResponse, error) {
 func NoContent() (events.APIGatewayProxyResponse, error) {
 	return events.APIGatewayProxyResponse{
 		StatusCode: http.StatusNoContent,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
 	}, nil
 }
 
@@ -45,7 +46,7 @@ func TooManyRequests(message string) (events.APIGatewayProxyResponse, error) {
 
 	return events.APIGatewayProxyResponse{
 		StatusCode: http.StatusTooManyRequests,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
 		Body:       string(responseJson),
 	}, nil
 }
@@ -85,7 +86,7 @@ func GenericResponse(statusCode int, body string, headers map[string]string) (ev
 	return events.APIGatewayProxyResponse{
 		StatusCode: statusCode,
 		Body:       body + "\n",
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), headers),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), headers),
 	}, nil
 }
 
@@ -101,7 +102,7 @@ func MaintenanceResponse(message string) events.APIGatewayProxyResponse {
 	return events.APIGatewayProxyResponse{
 		StatusCode: http.StatusTeapot,
 		Body:       message,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
 	}
 }
 
@@ -109,13 +110,13 @@ func RateLimitResponse(message string) events.APIGatewayProxyResponse {
 	return events.APIGatewayProxyResponse{
 		StatusCode: http.StatusTooManyRequests,
 		Body:       message,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
 	}
 }
 
 func OptionsResponse() events.APIGatewayProxyResponse {
 	return events.APIGatewayProxyResponse{
 		StatusCode: http.StatusNoContent,
-		Headers:    utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
+		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), ContentTypeJSONHeader),
 	}
 }
diff --git a/slice_utils/slice_utils.go b/slice_utils/slice_utils.go
index 0c400cb7863bf42ead48dfee4ee6b57464347de5..f203d0dc8d0dd698f63a5e53a815a990fb005aff 100644
--- a/slice_utils/slice_utils.go
+++ b/slice_utils/slice_utils.go
@@ -53,3 +53,14 @@ func FilterNonEmptyString(arr []string) []string {
 	}).([]string)
 	return nonEmptyStrings
 }
+
+func ArraySlice(s []any, offset, length int) []any {
+	if offset > len(s) {
+		return s
+	}
+	end := offset + length
+	if end < len(s) {
+		return s[offset:end]
+	}
+	return s[offset:]
+}
diff --git a/string_utils/string_utils.go b/string_utils/string_utils.go
index 9b4cafe14ca1760701000f80aa1af10f5eaf6e8a..995e94a309eabbf18f1bee3cd9462242d930a2c4 100644
--- a/string_utils/string_utils.go
+++ b/string_utils/string_utils.go
@@ -4,9 +4,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
-	"net/mail"
-	"net/url"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/number_utils"
 	"regexp"
 	"strconv"
 	"strings"
@@ -68,37 +66,6 @@ func IsNumericString(s string) bool {
 	return err == nil
 }
 
-// StandardisePhoneNumber standardises phone numbers with +27 instead of 0 prefix
-func StandardisePhoneNumber(number string) string {
-	number = strings.TrimSpace(number)
-
-	if number == "" {
-		return number
-	}
-
-	// is the first rune/char of the string a 0
-	if []rune(number)[0] == []rune("0")[0] {
-		// Add south african country code (hardcoded for now)
-		number = "+27" + number[1:]
-	}
-	return number
-}
-
-func FormatPhoneNumber(phoneNumber string) string {
-	if len(phoneNumber) > 7 {
-		return phoneNumber
-	}
-
-	// Format as 076 453 2188
-	phoneNumber = insertInto(phoneNumber, 3, " ")
-	phoneNumber = insertInto(phoneNumber, 7, " ")
-	return phoneNumber
-}
-
-func insertInto(s string, index int, characters string) string {
-	return s[:index] + characters + s[index:]
-}
-
 func IsAlphaNumeric(str string) bool {
 	regex := regexp.MustCompile("^[a-zA-Z0-9]*$")
 	return regex.MatchString(str)
@@ -109,22 +76,10 @@ func IsAlphaNumericOrDash(str string) bool {
 	return regex.MatchString(str)
 }
 
-func IsValidUsername(str string) bool {
-	regex := regexp.MustCompile("^[a-zA-Z0-9-.@+_]*$")
-	return regex.MatchString(str)
-}
-
 func Equal(a string, b string) bool {
 	return strings.TrimSpace(strings.ToLower(a)) == strings.TrimSpace(strings.ToLower(b))
 }
 
-func UnwrapString(s *string) string {
-	if s == nil {
-		return ""
-	}
-	return *s
-}
-
 // TrimP trims specified strings, replacing empty string with nil
 func TrimP(sp *string) *string {
 	if sp == nil {
@@ -180,9 +135,6 @@ func Int64ToString(number int64) string {
 func IntToString(number int) string {
 	return strconv.Itoa(number)
 }
-func StringToInt(stringValue string) (int, error) {
-	return strconv.Atoi(stringValue)
-}
 
 func Int64SliceToString(numbers []int64) string {
 	numString := fmt.Sprint(numbers)
@@ -198,19 +150,6 @@ func Int64SliceToStringSlice(numbers []int64) []string {
 	return numStringSlice
 }
 
-func StringToInt64(stringValue string) (int64, error) {
-	number, err := strconv.ParseInt(stringValue, 10, 64)
-	return number, err
-}
-
-func StringToFloat64(stringValue string) (float64, error) {
-	number, err := strconv.ParseFloat(stringValue, 64)
-	if err != nil {
-		return 0, err
-	}
-	return number, nil
-}
-
 func Float64ToString(number float64, precision int) string {
 	return strconv.FormatFloat(number, 'f', precision, 64)
 }
@@ -220,18 +159,10 @@ func Float64ToStringWithPrec(number float64, prec int) string {
 }
 
 func ValidateStringAsInt64(stringValue string) error {
-	_, err := StringToInt64(stringValue)
+	_, err := number_utils.StringToInt64(stringValue)
 	return err
 }
 
-func PtoString(stringPointer *string) string {
-	if stringPointer == nil {
-		return ""
-	}
-
-	return *stringPointer
-}
-
 func IsEmpty(sp *string) bool {
 	if sp == nil {
 		return true
@@ -269,35 +200,6 @@ func SentenceCase(str string) string {
 	return ""
 }
 
-// RemoveUrlScheme Removes http:// or https:// from a URL
-func RemoveUrlScheme(str string) string {
-	newStr := strings.Replace(str, "http://", "", 1)
-	newStr = strings.Replace(str, "https://", "", 1)
-	return newStr
-}
-
-// EscapeOpenSearchSearchString See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_reserved_characters
-func EscapeOpenSearchSearchString(str string) string {
-	searchString := str
-
-	// Reserved characters
-	// NOTE: first char must be "\" to prevent replacing it again after replacing other chars with "\\"
-	reservedCharacters := []string{"\\", "+", "-", "=", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", "*", "?", ":", "/"}
-
-	// Remove "<" and ">"
-	strings.ReplaceAll(searchString, "<", "")
-	strings.ReplaceAll(searchString, ">", "")
-
-	// Escape the reserved characters with double backslashes ("\\")
-	for _, char := range reservedCharacters {
-		if strings.Contains(searchString, char) {
-			re := regexp.MustCompile(char)
-			searchString = re.ReplaceAllString(searchString, "\\"+char)
-		}
-	}
-	return searchString
-}
-
 // SplitString separates a string on any character in the list of sep
 func SplitString(str string, sep []rune) []string {
 	splitStrings := strings.FieldsFunc(str, func(c rune) bool {
@@ -307,12 +209,6 @@ func SplitString(str string, sep []rune) []string {
 	return splitStrings
 }
 
-// IsUrlStrict Returns whether a URL is valid in a strict way (Must have scheme and host)
-func IsUrlStrict(str string) bool {
-	u, err := url.Parse(str)
-	return err == nil && u.Scheme != "" && u.Host != ""
-}
-
 func LimitStringToMaxLength(str string, maxLen int) string {
 	if len(str) > maxLen {
 		str = str[0:maxLen]
@@ -320,33 +216,6 @@ func LimitStringToMaxLength(str string, maxLen int) string {
 	return str
 }
 
-// StripQueryString - Strips the query parameters from a URL
-func StripQueryString(inputUrl string) (string, error) {
-	u, err := url.Parse(inputUrl)
-	if err != nil {
-		return inputUrl, err
-	}
-	u.RawQuery = ""
-	return u.String(), nil
-}
-
-func ValidateEmailAddress(email string) (string, error) {
-	if email == "" {
-		return "", errors.Error("email address is empty")
-	}
-
-	cleanEmail := strings.ToLower(strings.TrimSpace(email))
-	cleanEmail = RemoveAllWhiteSpaces(cleanEmail)
-
-	// We validate it but still return it since in some cases we don't want to break everything if the email is bad
-	_, err := mail.ParseAddress(cleanEmail)
-	if err != nil {
-		return cleanEmail, errors.Wrap(err, "could not parse email address")
-	}
-
-	return cleanEmail, nil
-}
-
 func PascalCaseToSentence(pascal string) string {
 	var parts []string
 	start := 0
diff --git a/utils/utils.go b/utils/utils.go
index 165a1dc575c6810c954bd04de3ccbd159bb9c0bc..b537f8163ddf17b48b95a2cd6565e0aed13c210e 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -3,10 +3,13 @@ package utils
 import (
 	"archive/zip"
 	"bytes"
-	"fmt"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/string_utils"
 	"io/ioutil"
+	"net/mail"
+	"net/url"
 	"os"
-	"strconv"
+	"regexp"
 	"strings"
 	"time"
 
@@ -101,67 +104,112 @@ func DeepCopy(fromValue interface{}) (toValue interface{}) {
 	return deepcopy.Copy(fromValue)
 }
 
-func UnwrapBool(b *bool) bool {
-	if b == nil {
-		return false
-	}
-	return *b
-}
-
-// MapStringInterfaceToMapStringString converts a generic value typed map to a map with string values
-func MapStringInterfaceToMapStringString(inputMap map[string]interface{}) map[string]string {
-	query := make(map[string]string)
-	for mapKey, mapVal := range inputMap {
-		// Check if mapVal is a slice or a single value
-		switch mapValTyped := mapVal.(type) {
-		case []interface{}:
-			// Slice - convert each element individually
-			var mapValString []string
-
-			// Loop through each element in the slice and check the type
-			for _, sliceElem := range mapValTyped {
-				switch sliceElemTyped := sliceElem.(type) {
-				case string:
-					// Enclose strings in escaped quotations
-					mapValString = append(mapValString, fmt.Sprintf("\"%v\"", sliceElemTyped))
-				case float64:
-					// Use FormatFloat for least amount of precision.
-					mapValString = append(mapValString, strconv.FormatFloat(sliceElemTyped, 'f', -1, 64))
-				default:
-					// Convert to string
-					mapValString = append(mapValString, fmt.Sprintf("%v", sliceElemTyped))
-				}
-			}
-			// Join as a comma seperated array
-			query[mapKey] = "[" + strings.Join(mapValString, ",") + "]"
-		default:
-			// Single value - convert to string
-			query[mapKey] = fmt.Sprintf("%v", mapVal)
-		}
+func ValueToPointer[V any](value V) *V {
+	return &value
+}
+
+func PointerToValue[V any](value *V) V {
+	if value != nil {
+		return *value
 	}
 
-	return query
+	return *new(V) // zero value of V
 }
 
-// MergeMaps If there are similar properties in the maps, the last one will be used as the value
-func MergeMaps(maps ...map[string]string) map[string]string {
-	ret := map[string]string{}
-	for _, mapV := range maps {
-		for k, v := range mapV {
-			ret[k] = v
-		}
+func ValidateEmailAddress(email string) (string, error) {
+	if email == "" {
+		return "", errors.Error("email address is empty")
+	}
+
+	cleanEmail := strings.ToLower(strings.TrimSpace(email))
+	cleanEmail = string_utils.RemoveAllWhiteSpaces(cleanEmail)
+
+	// We validate it but still return it since in some cases we don't want to break everything if the email is bad
+	_, err := mail.ParseAddress(cleanEmail)
+	if err != nil {
+		return cleanEmail, errors.Wrap(err, "could not parse email address")
 	}
-	return ret
+
+	return cleanEmail, nil
 }
 
-func PointerValue[V any](value V) *V {
-	return &value
+// IsUrlStrict Returns whether a URL is valid in a strict way (Must have scheme and host)
+func IsUrlStrict(str string) bool {
+	u, err := url.Parse(str)
+	return err == nil && u.Scheme != "" && u.Host != ""
 }
 
-func Unwrap[V any](value *V) V {
-	if value != nil {
-		return *value
+func IsValidUsername(str string) bool {
+	regex := regexp.MustCompile("^[a-zA-Z0-9-.@+_]*$")
+	return regex.MatchString(str)
+}
+
+// StandardisePhoneNumber standardises phone numbers with +27 instead of 0 prefix
+func StandardisePhoneNumber(number string) string {
+	number = strings.TrimSpace(number)
+
+	if number == "" {
+		return number
 	}
 
-	return *new(V) // zero value of V
+	// is the first rune/char of the string a 0
+	if []rune(number)[0] == []rune("0")[0] {
+		// Add south african country code (hardcoded for now)
+		number = "+27" + number[1:]
+	}
+	return number
+}
+
+func FormatPhoneNumber(phoneNumber string) string {
+	if len(phoneNumber) > 7 {
+		return phoneNumber
+	}
+
+	// Format as 076 453 2188
+	phoneNumber = insertInto(phoneNumber, 3, " ")
+	phoneNumber = insertInto(phoneNumber, 7, " ")
+	return phoneNumber
+}
+
+func insertInto(s string, index int, characters string) string {
+	return s[:index] + characters + s[index:]
+}
+
+// RemoveUrlScheme Removes http:// or https:// from a URL
+func RemoveUrlScheme(str string) string {
+	newStr := strings.Replace(str, "http://", "", 1)
+	newStr = strings.Replace(str, "https://", "", 1)
+	return newStr
+}
+
+// StripQueryString - Strips the query parameters from a URL
+func StripQueryString(inputUrl string) (string, error) {
+	u, err := url.Parse(inputUrl)
+	if err != nil {
+		return inputUrl, err
+	}
+	u.RawQuery = ""
+	return u.String(), nil
+}
+
+// EscapeOpenSearchSearchString See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_reserved_characters
+func EscapeOpenSearchSearchString(str string) string {
+	searchString := str
+
+	// Reserved characters
+	// NOTE: first char must be "\" to prevent replacing it again after replacing other chars with "\\"
+	reservedCharacters := []string{"\\", "+", "-", "=", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", "*", "?", ":", "/"}
+
+	// Remove "<" and ">"
+	strings.ReplaceAll(searchString, "<", "")
+	strings.ReplaceAll(searchString, ">", "")
+
+	// Escape the reserved characters with double backslashes ("\\")
+	for _, char := range reservedCharacters {
+		if strings.Contains(searchString, char) {
+			re := regexp.MustCompile(char)
+			searchString = re.ReplaceAllString(searchString, "\\"+char)
+		}
+	}
+	return searchString
 }