flatten.go
func flattenFix(keyvals []interface{}) []interface{} {
	// opinionated constants for names of keys
	const (
		keyMsg           = "msg"
		keyError         = "error"
		keyMissingPrefix = "_p"
	)

	// Indicates whether the keyvals slice needs to be flattened.
	// Start with true if it has an odd number of items.
	requiresFlattening := isOdd(len(keyvals))

	// Used for estimating the size of the flattened slice
	// in an attempt to use one memory allocation.
	var estimatedLen int

	// Do the keyvals include a "msg" key. This is not entirely
	// reliable if "msg" is passed as a value, but it is only used
	// as a heuristic for naming missing keys.
	var haveMsg bool

	for i, val := range keyvals {
		switch v := val.(type) {
		case List:
			requiresFlattening = true
			// TODO(jpj): recursively descending into the keyvals
			// will come up with a reasonably length estimate, but
			// for now just double the number of elements in the slice
			// and this is probably accurate enough.
			estimatedLen += len(v)
		case keyvalser:
			// unknown implementation: calling Keyvals could result in
			// additional memory allocation, so use a conservative guess
			requiresFlattening = true
			estimatedLen += 16
		case string:
			if v == keyMsg {
				// Remember that we already have a "msg" key, which
				// will be used for inferring missing key names later.
				haveMsg = true
			}
		default:
			estimatedLen++
			if isEven(i) {
				// Non-string in an even position could mean a missing
				// key name in the list.
				estimatedLen++
				requiresFlattening = true
			}
		}
	}

	if !requiresFlattening {
		// Nothing to do, so return the input unmodified.
		return keyvals
	}

	// The missingKey function is passed recursively to flattening
	// and fixing functions. After flattening and fixing we know if
	// one or more missing keys have been inserted, and then we know
	// to iterate through and name them in order.
	var hasMissingKeys bool
	missingKey := func() interface{} {
		hasMissingKeys = true
		return missingKeyT("MISSING")
	}

	// In most circumstances this output slice will have the
	// required capacity.
	output := make([]interface{}, 0, estimatedLen)

	// Perform the actual flattening and fixing.
	output = flatten(output, keyvals, missingKey)

	// If there were any missing keys inserted, iterate through the
	// list and name them. Doing this last allows the names to be
	// ordered from left to right.
	if hasMissingKeys {
		counter := 0 // used for counting _p1, _p2, etc
		for i, v := range output {
			if _, ok := v.(missingKeyT); ok {
				// assign a name for the missing key, depends on the type
				// of the value associated with the key
				var keyName string
				switch output[i+1].(type) {
				case string:
					if !haveMsg {
						// If there is no 'msg' key, the first string
						// value gets 'msg' as its key.
						haveMsg = true
						keyName = keyMsg
					}
				case error:
					if haveMsg {
						// If there is already a 'msg' key, then an
						// error gets 'error' as the key.
						keyName = keyError
					} else {
						// If there is no 'msg' key, the first error
						// value gets 'msg' as its key.
						haveMsg = true
						keyName = keyMsg
					}
				}
				if keyName == "" {
					// Otherwise, missing keys all have a prefix that is
					// unlikely to clash with others key names, and are
					// numbered from 1.
					counter++
					keyName = keyMissingPrefix + strconv.Itoa(counter)
				}
				output[i] = keyName
			}
		}
	}

	return output
}
func flattenFix(keyvals []interface{}) []interface{} {
	// opinionated constants for names of keys
	const (
		keyMsg           = "msg"
		keyError         = "error"
		keyMissingPrefix = "_p"
	)

	// Indicates whether the keyvals slice needs to be flattened.
	// Start with true if it has an odd number of items.
	requiresFlattening := isOdd(len(keyvals))

	// Used for estimating the size of the flattened slice
	// in an attempt to use one memory allocation.
	var estimatedLen int

	// Do the keyvals include a "msg" key. This is not entirely
	// reliable if "msg" is passed as a value, but it is only used
	// as a heuristic for naming missing keys.
	var haveMsg bool

	for i, val := range keyvals {
		switch v := val.(type) {
		case List:
			requiresFlattening = true
			// TODO(jpj): recursively descending into the keyvals
			// will come up with a reasonably length estimate, but
			// for now just double the number of elements in the slice
			// and this is probably accurate enough.
			estimatedLen += len(v)
		case keyvalser:
			// unknown implementation: calling Keyvals could result in
			// additional memory allocation, so use a conservative guess
			requiresFlattening = true
			estimatedLen += 16
		case string:
			if v == keyMsg {
				// Remember that we already have a "msg" key, which
				// will be used for inferring missing key names later.
				haveMsg = true
			}
		default:
			estimatedLen++
			if isEven(i) {
				// Non-string in an even position could mean a missing
				// key name in the list.
				estimatedLen++
				requiresFlattening = true
			}
		}
	}

	if !requiresFlattening {
		// Nothing to do, so return the input unmodified.
		return keyvals
	}

	// The missingKey function is passed recursively to flattening
	// and fixing functions. After flattening and fixing we know if
	// one or more missing keys have been inserted, and then we know
	// to iterate through and name them in order.
	var hasMissingKeys bool
	missingKey := func() interface{} {
		hasMissingKeys = true
		return missingKeyT("MISSING")
	}

	// In most circumstances this output slice will have the
	// required capacity.
	output := make([]interface{}, 0, estimatedLen)

	// Perform the actual flattening and fixing.
	output = flatten(output, keyvals, missingKey)

	// If there were any missing keys inserted, iterate through the
	// list and name them. Doing this last allows the names to be
	// ordered from left to right.
	if hasMissingKeys {
		counter := 0 // used for counting _p1, _p2, etc
		for i, v := range output {
			if _, ok := v.(missingKeyT); ok {
				// assign a name for the missing key, depends on the type
				// of the value associated with the key
				var keyName string
				switch output[i+1].(type) {
				case string:
					if !haveMsg {
						// If there is no 'msg' key, the first string
						// value gets 'msg' as its key.
						haveMsg = true
						keyName = keyMsg
					}
				case error:
					if haveMsg {
						// If there is already a 'msg' key, then an
						// error gets 'error' as the key.
						keyName = keyError
					} else {
						// If there is no 'msg' key, the first error
						// value gets 'msg' as its key.
						haveMsg = true
						keyName = keyMsg
					}
				}
				if keyName == "" {
					// Otherwise, missing keys all have a prefix that is
					// unlikely to clash with others key names, and are
					// numbered from 1.
					counter++
					keyName = keyMissingPrefix + strconv.Itoa(counter)
				}
				output[i] = keyName
			}
		}
	}

	return output
}
func flattenFix ( keyvals [ ] interface { } ) [ ] interface { } { // opinionated constants for names of keys const ( keyMsg = " " keyError = " " keyMissingPrefix = " " ) // Indicates whether the keyvals slice needs to be flattened. // Start with true if it has an odd number of items. requiresFlattening := isOdd ( len ( keyvals ) ) // Used for estimating the size of the flattened slice // in an attempt to use one memory allocation. var estimatedLen int // Do the keyvals include a "msg" key. This is not entirely // reliable if "msg" is passed as a value, but it is only used // as a heuristic for naming missing keys. var haveMsg bool for i , val := range keyvals { switch v := val . ( type ) { case List : requiresFlattening = true // TODO(jpj): recursively descending into the keyvals // will come up with a reasonably length estimate, but // for now just double the number of elements in the slice // and this is probably accurate enough. estimatedLen += len ( v ) case keyvalser : // unknown implementation: calling Keyvals could result in // additional memory allocation, so use a conservative guess requiresFlattening = true estimatedLen += 16 case string : if v == keyMsg { // Remember that we already have a "msg" key, which // will be used for inferring missing key names later. haveMsg = true } default : estimatedLen ++ if isEven ( i ) { // Non-string in an even position could mean a missing // key name in the list. estimatedLen ++ requiresFlattening = true } } } if ! requiresFlattening { // Nothing to do, so return the input unmodified. return keyvals } // The missingKey function is passed recursively to flattening // and fixing functions. After flattening and fixing we know if // one or more missing keys have been inserted, and then we know // to iterate through and name them in order. var hasMissingKeys bool missingKey := func ( ) interface { } { hasMissingKeys = true return missingKeyT ( " " ) } // In most circumstances this output slice will have the // required capacity. output := make ( [ ] interface { } , 0 , estimatedLen ) // Perform the actual flattening and fixing. output = flatten ( output , keyvals , missingKey ) // If there were any missing keys inserted, iterate through the // list and name them. Doing this last allows the names to be // ordered from left to right. if hasMissingKeys { counter := 0 // used for counting _p1, _p2, etc for i , v := range output { if _ , ok := v . ( missingKeyT ) ; ok { // assign a name for the missing key, depends on the type // of the value associated with the key var keyName string switch output [ i + 1 ] . ( type ) { case string : if ! haveMsg { // If there is no 'msg' key, the first string // value gets 'msg' as its key. haveMsg = true keyName = keyMsg } case error : if haveMsg { // If there is already a 'msg' key, then an // error gets 'error' as the key. keyName = keyError } else { // If there is no 'msg' key, the first error // value gets 'msg' as its key. haveMsg = true keyName = keyMsg } } if keyName == " " { // Otherwise, missing keys all have a prefix that is // unlikely to clash with others key names, and are // numbered from 1. counter ++ keyName = keyMissingPrefix + strconv . Itoa ( counter ) } output [ i ] = keyName } } } return output }
// flattenFix accepts a keyvals slice and "flattens" it into a slice
// of alternating key/value pairs. See the examples.
//
// flattenFix will also check the result to ensure it is a valid
// slice of key/value pairs. The returned slice is guaranteed
// to have an even number of items, and every item at an even-numbered
// index is guaranteed to be a string.
flattenFix accepts a keyvals slice and flattens it into a slice of alternating key / value pairs . See the examples . flattenFix will also check the result to ensure it is a valid slice of key / value pairs . The returned slice is guaranteed to have an even number of items and every item at an even - numbered index is guaranteed to be a string .