github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/pkg/list/list.go (about)

     1  // Copyright 2019 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package list contains functions for manipulating and examining lists.
    16  package list
    17  
    18  import (
    19  	"fmt"
    20  	"sort"
    21  
    22  	"github.com/joomcode/cue/cue"
    23  )
    24  
    25  // Drop reports the suffix of list x after the first n elements,
    26  // or [] if n > len(x).
    27  //
    28  // For instance:
    29  //
    30  //    Drop([1, 2, 3, 4], 2)
    31  //
    32  // results in
    33  //
    34  //    [3, 4]
    35  //
    36  func Drop(x []cue.Value, n int) ([]cue.Value, error) {
    37  	if n < 0 {
    38  		return nil, fmt.Errorf("negative index")
    39  	}
    40  
    41  	if n > len(x) {
    42  		return []cue.Value{}, nil
    43  	}
    44  
    45  	return x[n:], nil
    46  }
    47  
    48  // TODO: disable Flatten until we know the right default for depth.
    49  //       The right time to determine is at least some point after the query
    50  //       extensions are introduced, which may provide flatten functionality
    51  //       natively.
    52  //
    53  // // Flatten reports a flattend sequence of the list xs by expanding any elements
    54  // // that are lists.
    55  // //
    56  // // For instance:
    57  // //
    58  // //    Flatten([1, [[2, 3], []], [4]])
    59  // //
    60  // // results in
    61  // //
    62  // //    [1, 2, 3, 4]
    63  // //
    64  // func Flatten(xs cue.Value) ([]cue.Value, error) {
    65  // 	var flatten func(cue.Value) ([]cue.Value, error)
    66  // 	flatten = func(xs cue.Value) ([]cue.Value, error) {
    67  // 		var res []cue.Value
    68  // 		iter, err := xs.List()
    69  // 		if err != nil {
    70  // 			return nil, err
    71  // 		}
    72  // 		for iter.Next() {
    73  // 			val := iter.Value()
    74  // 			if val.Kind() == cue.ListKind {
    75  // 				vals, err := flatten(val)
    76  // 				if err != nil {
    77  // 					return nil, err
    78  // 				}
    79  // 				res = append(res, vals...)
    80  // 			} else {
    81  // 				res = append(res, val)
    82  // 			}
    83  // 		}
    84  // 		return res, nil
    85  // 	}
    86  // 	return flatten(xs)
    87  // }
    88  
    89  // FlattenN reports a flattend sequence of the list xs by expanding any elements
    90  // depth levels deep. If depth is negative all elements are expanded.
    91  //
    92  // For instance:
    93  //
    94  //    FlattenN([1, [[2, 3], []], [4]], 1)
    95  //
    96  // results in
    97  //
    98  //    [1, [2, 3], [], 4]
    99  //
   100  func FlattenN(xs cue.Value, depth int) ([]cue.Value, error) {
   101  	var flattenN func(cue.Value, int) ([]cue.Value, error)
   102  	flattenN = func(xs cue.Value, depth int) ([]cue.Value, error) {
   103  		var res []cue.Value
   104  		iter, err := xs.List()
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  		for iter.Next() {
   109  			val, _ := iter.Value().Default()
   110  			if val.Kind() == cue.ListKind && depth != 0 {
   111  				d := depth - 1
   112  				values, err := flattenN(val, d)
   113  				if err != nil {
   114  					return nil, err
   115  				}
   116  				res = append(res, values...)
   117  			} else {
   118  				res = append(res, val)
   119  			}
   120  		}
   121  		return res, nil
   122  	}
   123  	return flattenN(xs, depth)
   124  }
   125  
   126  // Repeat returns a new list consisting of count copies of list x.
   127  //
   128  // For instance:
   129  //
   130  //    Repeat([1, 2], 2)
   131  //
   132  // results in
   133  //
   134  //    [1, 2, 1, 2]
   135  //
   136  func Repeat(x []cue.Value, count int) ([]cue.Value, error) {
   137  	if count < 0 {
   138  		return nil, fmt.Errorf("negative count")
   139  	}
   140  	var a []cue.Value
   141  	for i := 0; i < count; i++ {
   142  		a = append(a, x...)
   143  	}
   144  	return a, nil
   145  }
   146  
   147  // Concat takes a list of lists and concatenates them.
   148  //
   149  // Concat([a, b, c]) is equivalent to
   150  //
   151  //     [ for x in a {x}, for x in b {x}, for x in c {x} ]
   152  //
   153  func Concat(a []cue.Value) ([]cue.Value, error) {
   154  	var res []cue.Value
   155  	for _, e := range a {
   156  		iter, err := e.List()
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  		for iter.Next() {
   161  			res = append(res, iter.Value())
   162  		}
   163  	}
   164  	return res, nil
   165  }
   166  
   167  // Take reports the prefix of length n of list x, or x itself if n > len(x).
   168  //
   169  // For instance:
   170  //
   171  //    Take([1, 2, 3, 4], 2)
   172  //
   173  // results in
   174  //
   175  //    [1, 2]
   176  //
   177  func Take(x []cue.Value, n int) ([]cue.Value, error) {
   178  	if n < 0 {
   179  		return nil, fmt.Errorf("negative index")
   180  	}
   181  
   182  	if n > len(x) {
   183  		return x, nil
   184  	}
   185  
   186  	return x[:n], nil
   187  }
   188  
   189  // Slice extracts the consecutive elements from list x starting from position i
   190  // up till, but not including, position j, where 0 <= i < j <= len(x).
   191  //
   192  // For instance:
   193  //
   194  //    Slice([1, 2, 3, 4], 1, 3)
   195  //
   196  // results in
   197  //
   198  //    [2, 3]
   199  //
   200  func Slice(x []cue.Value, i, j int) ([]cue.Value, error) {
   201  	if i < 0 {
   202  		return nil, fmt.Errorf("negative index")
   203  	}
   204  
   205  	if i > j {
   206  		return nil, fmt.Errorf("invalid index: %v > %v", i, j)
   207  	}
   208  
   209  	if i > len(x) {
   210  		return nil, fmt.Errorf("slice bounds out of range")
   211  	}
   212  
   213  	if j > len(x) {
   214  		return nil, fmt.Errorf("slice bounds out of range")
   215  	}
   216  
   217  	return x[i:j], nil
   218  }
   219  
   220  // MinItems reports whether a has at least n items.
   221  func MinItems(a []cue.Value, n int) bool {
   222  	return len(a) >= n
   223  }
   224  
   225  // MaxItems reports whether a has at most n items.
   226  func MaxItems(a []cue.Value, n int) bool {
   227  	return len(a) <= n
   228  }
   229  
   230  // UniqueItems reports whether all elements in the list are unique.
   231  func UniqueItems(a []cue.Value) bool {
   232  	b := []string{}
   233  	for _, v := range a {
   234  		b = append(b, fmt.Sprintf("%+v", v))
   235  	}
   236  	sort.Strings(b)
   237  	for i := 1; i < len(b); i++ {
   238  		if b[i-1] == b[i] {
   239  			return false
   240  		}
   241  	}
   242  	return true
   243  }
   244  
   245  // Contains reports whether v is contained in a. The value must be a
   246  // comparable value.
   247  func Contains(a []cue.Value, v cue.Value) bool {
   248  	for _, w := range a {
   249  		if v.Equals(w) {
   250  			return true
   251  		}
   252  	}
   253  	return false
   254  }