github.com/haraldrudell/parl@v0.4.176/sets/map-set.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package sets
     7  
     8  import (
     9  	"fmt"
    10  	"slices"
    11  
    12  	"github.com/haraldrudell/parl/iters"
    13  	"github.com/haraldrudell/parl/pfmt"
    14  	"github.com/haraldrudell/parl/plog"
    15  	"golang.org/x/exp/constraints"
    16  )
    17  
    18  type MapSet[T constraints.Ordered, E any] struct {
    19  	elementMap map[T]E
    20  	elements   []sorter[T, E]
    21  }
    22  
    23  type sorter[T constraints.Ordered, E any] struct {
    24  	t T
    25  	e E
    26  }
    27  
    28  func NewMapSet[T constraints.Ordered, E any](m map[T]E) (set SetID[T, E]) {
    29  
    30  	// sort elements
    31  	//	- no pointers is 1 allocation
    32  	var sortable = make([]sorter[T, E], len(m))
    33  	var i int
    34  	for t, e := range m {
    35  		sortable[i] = sorter[T, E]{t: t, e: e}
    36  		i++
    37  	}
    38  	slices.SortFunc(sortable, sorterCmp[T, E])
    39  
    40  	return &MapSet[T, E]{
    41  		elementMap: m,
    42  		elements:   sortable,
    43  	}
    44  }
    45  
    46  // IsValid returns whether value is part of the set
    47  func (s *MapSet[T, E]) IsValid(value T) (isValid bool) {
    48  	_, isValid = s.elementMap[value]
    49  	return
    50  }
    51  
    52  // Iterator allows iteration over all set elements
    53  func (s *MapSet[T, E]) Iterator() (iterator iters.Iterator[T]) {
    54  	return iters.NewSimpleConverterIterator(
    55  		iters.NewSlicePointerIterator(s.elements),
    56  		s.convertT,
    57  	)
    58  }
    59  
    60  // StringT returns a string representation for an element of this set.
    61  // If value is not a valid element, a fmt.Printf value is output like ?'%v'
    62  func (s *MapSet[T, E]) StringT(value T) (s2 string) {
    63  	var e, ok = s.elementMap[value]
    64  	if !ok {
    65  		s2 = fmt.Sprintf("?badID‘%v’", value)
    66  		return
    67  	}
    68  	// StringT is intended to be the String method of a named type implementing set.
    69  	// if StringT method code would somehow invoke the T.String method again,
    70  	// this will cause infinite recursion and stack overflow panic.
    71  	s2 = pfmt.NoRecurseVPrint(e)
    72  
    73  	return
    74  }
    75  
    76  // Description returns a more elaborate string representation
    77  // for an element. Description and StringT may return the same value
    78  func (s *MapSet[T, E]) Description(value T) (full string) {
    79  	var e, ok = s.elementMap[value]
    80  	if !ok {
    81  		return
    82  	}
    83  	// type assert to Element with Description method
    84  	var elementFull ElementDescription
    85  	if elementFull, ok = any(&e).(ElementDescription); !ok {
    86  		return // not a full element return
    87  	}
    88  
    89  	full = elementFull.Description()
    90  
    91  	return
    92  }
    93  
    94  // Element returns the element representation for value or
    95  // nil if value is not an element of the set
    96  func (s *MapSet[T, E]) Element(value T) (elementType *E) {
    97  	var e, ok = s.elementMap[value]
    98  	if !ok {
    99  		return
   100  	}
   101  	elementType = &e
   102  
   103  	return
   104  }
   105  
   106  // Iterator allows iteration over all set elements
   107  func (s *MapSet[T, E]) EIterator() (iterator iters.Iterator[*E]) {
   108  	return iters.NewSimpleConverterIterator(
   109  		iters.NewSlicePointerIterator(s.elements),
   110  		s.convertE,
   111  	)
   112  
   113  }
   114  
   115  func (s *MapSet[T, E]) convertT(tePair *sorter[T, E]) (value T) { return tePair.t }
   116  
   117  func (s *MapSet[T, E]) convertE(tePair *sorter[T, E]) (elementType *E) { return &tePair.e }
   118  
   119  func (s *MapSet[T, E]) String() (s2 string) {
   120  	var e E
   121  	s2 = plog.Sprintf("mapSet_%T:%d", e, len(s.elementMap))
   122  	return
   123  }
   124  
   125  func sorterCmp[T constraints.Ordered, E any](a, b sorter[T, E]) (result int) {
   126  	if a.t < b.t {
   127  		return -1
   128  	} else if a.t > b.t {
   129  		return 1
   130  	}
   131  	return
   132  }