github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/structure/sets/linkedhashset/linkedhashset.go (about)

     1  // Package linkedhashset is a set that preserves insertion-order.
     2  //
     3  // It is backed by a hash table to store values and doubly-linked list to store ordering.
     4  //
     5  // Note that insertion-order is not affected if an element is re-inserted into the set.
     6  //
     7  // Structure is not thread safe.
     8  //
     9  // References: http://en.wikipedia.org/wiki/Set_%28abstract_data_type%29
    10  package linkedhashset
    11  
    12  import (
    13  	"encoding/json"
    14  	"fmt"
    15  	"strings"
    16  
    17  	"github.com/songzhibin97/go-baseutils/structure/lists/doublylinkedlist"
    18  	"github.com/songzhibin97/go-baseutils/structure/sets"
    19  )
    20  
    21  // Assert Set implementation
    22  var _ sets.Set[int] = (*Set[int])(nil)
    23  
    24  // Set holds elements in go's native map
    25  type Set[E comparable] struct {
    26  	table    map[E]struct{}
    27  	ordering *doublylinkedlist.List[E]
    28  	zero     E
    29  }
    30  
    31  var itemExists = struct{}{}
    32  
    33  // New instantiates a new empty set and adds the passed values, if any, to the set
    34  func New[E comparable](values ...E) *Set[E] {
    35  	set := &Set[E]{
    36  		table:    make(map[E]struct{}),
    37  		ordering: doublylinkedlist.New[E](),
    38  	}
    39  	if len(values) > 0 {
    40  		set.Add(values...)
    41  	}
    42  	return set
    43  }
    44  
    45  // Add adds the items (one or more) to the set.
    46  // Note that insertion-order is not affected if an element is re-inserted into the set.
    47  func (set *Set[E]) Add(items ...E) {
    48  	for _, item := range items {
    49  		if _, contains := set.table[item]; !contains {
    50  			set.table[item] = itemExists
    51  			set.ordering.Append(item)
    52  		}
    53  	}
    54  }
    55  
    56  // Remove removes the items (one or more) from the set.
    57  // Slow operation, worst-case O(n^2).
    58  func (set *Set[E]) Remove(items ...E) {
    59  	for _, item := range items {
    60  		if _, contains := set.table[item]; contains {
    61  			delete(set.table, item)
    62  			index := set.ordering.IndexOf(item)
    63  			set.ordering.Remove(index)
    64  		}
    65  	}
    66  }
    67  
    68  // Contains check if items (one or more) are present in the set.
    69  // All items have to be present in the set for the method to return true.
    70  // Returns true if no arguments are passed at all, i.e. set is always superset of empty set.
    71  func (set *Set[E]) Contains(items ...E) bool {
    72  	for _, item := range items {
    73  		if _, contains := set.table[item]; !contains {
    74  			return false
    75  		}
    76  	}
    77  	return true
    78  }
    79  
    80  // Empty returns true if set does not contain any elements.
    81  func (set *Set[E]) Empty() bool {
    82  	return set.Size() == 0
    83  }
    84  
    85  // Size returns number of elements within the set.
    86  func (set *Set[E]) Size() int {
    87  	return set.ordering.Size()
    88  }
    89  
    90  // Clear clears all values in the set.
    91  func (set *Set[E]) Clear() {
    92  	set.table = make(map[E]struct{})
    93  	set.ordering.Clear()
    94  }
    95  
    96  // Values returns all items in the set.
    97  func (set *Set[E]) Values() []E {
    98  	values := make([]E, set.Size())
    99  	it := set.Iterator()
   100  	for it.Next() {
   101  		values[it.Index()] = it.Value()
   102  	}
   103  	return values
   104  }
   105  
   106  // String returns a string representation of container
   107  func (set *Set[E]) String() string {
   108  	b := strings.Builder{}
   109  	b.WriteString("LinkedHashSet\n")
   110  	it := set.Iterator()
   111  	for it.Next() {
   112  		b.WriteString(fmt.Sprintf("(key:%v) ", it.Value()))
   113  	}
   114  	return b.String()
   115  }
   116  
   117  // Intersection returns the intersection between two sets.
   118  // The new set consists of all elements that are both in "set" and "another".
   119  // Ref: https://en.wikipedia.org/wiki/Intersection_(set_theory)
   120  func (set *Set[E]) Intersection(another *Set[E]) *Set[E] {
   121  	result := New[E]()
   122  
   123  	// Iterate over smaller set (optimization)
   124  	if set.Size() <= another.Size() {
   125  		for item := range set.table {
   126  			if _, contains := another.table[item]; contains {
   127  				result.Add(item)
   128  			}
   129  		}
   130  	} else {
   131  		for item := range another.table {
   132  			if _, contains := set.table[item]; contains {
   133  				result.Add(item)
   134  			}
   135  		}
   136  	}
   137  
   138  	return result
   139  }
   140  
   141  // Union returns the union of two sets.
   142  // The new set consists of all elements that are in "set" or "another" (possibly both).
   143  // Ref: https://en.wikipedia.org/wiki/Union_(set_theory)
   144  func (set *Set[E]) Union(another *Set[E]) *Set[E] {
   145  	result := New[E]()
   146  
   147  	for item := range set.table {
   148  		result.Add(item)
   149  	}
   150  	for item := range another.table {
   151  		result.Add(item)
   152  	}
   153  
   154  	return result
   155  }
   156  
   157  // Difference returns the difference between two sets.
   158  // The new set consists of all elements that are in "set" but not in "another".
   159  // Ref: https://proofwiki.org/wiki/Definition:Set_Difference
   160  func (set *Set[E]) Difference(another *Set[E]) *Set[E] {
   161  	result := New[E]()
   162  
   163  	for item := range set.table {
   164  		if _, contains := another.table[item]; !contains {
   165  			result.Add(item)
   166  		}
   167  	}
   168  
   169  	return result
   170  }
   171  
   172  // UnmarshalJSON @implements json.Unmarshaler
   173  func (set *Set[E]) UnmarshalJSON(bytes []byte) error {
   174  	elements := []E{}
   175  	err := json.Unmarshal(bytes, &elements)
   176  	if err == nil {
   177  		set.Clear()
   178  		set.Add(elements...)
   179  	}
   180  	return err
   181  }
   182  
   183  // MarshalJSON @implements json.Marshaler
   184  func (set *Set[E]) MarshalJSON() ([]byte, error) {
   185  	return json.Marshal(set.Values())
   186  }