src/vec/select.php (166 lines of code) (raw):
<?hh
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the hphp/hsl/ subdirectory of this source tree.
*
*/
namespace HH\Lib\Vec;
use namespace HH\Lib\{C, Dict, Keyset, _Private};
/**
* Returns a new vec containing only the elements of the first Traversable that
* do not appear in any of the other ones.
*
* For vecs that contain non-arraykey elements, see `Vec\diff_by()`.
*
* Time complexity: O(n + m), where n is size of `$first` and m is the combined
* size of `$second` plus all the `...$rest`
* Space complexity: O(n + m), where n is size of `$first` and m is the combined
* size of `$second` plus all the `...$rest` -- note that this is bigger than
* O(n)
*/
function diff<Tv1 as arraykey, Tv2 as arraykey>(
Traversable<Tv1> $first,
Traversable<Tv2> $second,
Container<Tv2> ...$rest
)[]: vec<Tv1> {
if (!$first) {
return vec[];
}
if (!$second && !$rest) {
return cast_clear_legacy_array_mark($first);
}
$union = !$rest
? keyset($second)
: Keyset\union($second, ...$rest);
return filter(
$first,
($value) ==> !C\contains_key($union, $value),
);
}
/**
* Returns a new vec containing only the elements of the first Traversable
* that do not appear in the second one, where an element's identity is
* determined by the scalar function.
*
* For vecs that contain arraykey elements, see `Vec\diff()`.
*
* Time complexity: O((n + m) * s), where n is the size of `$first`, m is the
* size of `$second`, and s is the complexity of `$scalar_func`
* Space complexity: O(n + m), where n is the size of `$first` and m is the size
* of `$second` -- note that this is bigger than O(n)
*/
function diff_by<Tv, Ts as arraykey>(
Traversable<Tv> $first,
Traversable<Tv> $second,
(function(Tv)[_]: Ts) $scalar_func,
)[ctx $scalar_func]: vec<Tv> {
if (!$first) {
return vec[];
}
if (!$second) {
return cast_clear_legacy_array_mark($first);
}
$set = Keyset\map($second, $scalar_func);
return filter(
$first,
($value) ==> !C\contains_key($set, $scalar_func($value)),
);
}
/**
* Returns a new vec containing all except the first `$n` elements of the
* given Traversable.
*
* To take only the first `$n` elements, see `Vec\take()`.
*
* Time complexity: O(n), where n is the size of `$traversable`
* Space complexity: O(n), where n is the size of `$traversable`
*/
function drop<Tv>(
Traversable<Tv> $traversable,
int $n,
)[]: vec<Tv> {
invariant($n >= 0, 'Expected non-negative N, got %d.', $n);
$result = vec[];
$ii = -1;
foreach ($traversable as $value) {
$ii++;
if ($ii < $n) {
continue;
}
$result[] = $value;
}
return $result;
}
/**
* Returns a new vec containing only the values for which the given predicate
* returns `true`. The default predicate is casting the value to boolean.
*
* - To remove null values in a typechecker-visible way, see
* `Vec\filter_nulls()`.
* - To use an async predicate, see `Vec\filter_async()`.
*
* Time complexity: O(n * p), where p is the complexity of `$value_predicate`
* Space complexity: O(n)
*/
function filter<Tv>(
Traversable<Tv> $traversable,
?(function(Tv)[_]: bool) $value_predicate = null,
)[ctx $value_predicate]: vec<Tv> {
$value_predicate ??= _Private\boolval<>;
$result = vec[];
foreach ($traversable as $value) {
if ($value_predicate($value)) {
$result[] = $value;
}
}
return $result;
}
/**
* Returns a new vec containing only non-null values of the given
* Traversable.
*
* Time complexity: O(n)
* Space complexity: O(n)
*/
function filter_nulls<Tv>(
Traversable<?Tv> $traversable,
)[]: vec<Tv> {
$result = vec[];
foreach ($traversable as $value) {
if ($value !== null) {
$result[] = $value;
}
}
return $result;
}
/**
* Returns a new vec containing only the values for which the given predicate
* returns `true`.
*
* If you don't need access to the key, see `Vec\filter()`.
*
* Time complexity: O(n * p), where p is the complexity of `$predicate`
* Space complexity: O(n)
*/
function filter_with_key<Tk, Tv>(
KeyedTraversable<Tk, Tv> $traversable,
(function(Tk, Tv)[_]: bool) $predicate,
)[ctx $predicate]: vec<Tv> {
$result = vec[];
foreach ($traversable as $key => $value) {
if ($predicate($key, $value)) {
$result[] = $value;
}
}
return $result;
}
/**
* Returns a new vec containing only the elements of the first Traversable that
* appear in all the other ones. Duplicate values are preserved.
*
* Time complexity: O(n + m), where n is size of `$first` and m is the combined
* size of `$second` plus all the `...$rest`
* Space complexity: O(n), where n is size of `$first`
*/
function intersect<Tv as arraykey>(
Traversable<Tv> $first,
Traversable<Tv> $second,
Container<Tv> ...$rest
)[]: vec<Tv> {
$intersection = Keyset\intersect($first, $second, ...$rest);
if (!$intersection) {
return vec[];
}
return filter(
$first,
($value) ==> C\contains_key($intersection, $value),
);
}
/**
* Returns a new vec containing the keys of the given KeyedTraversable.
*
* Time complexity: O(n)
* Space complexity: O(n)
*/
function keys<Tk, Tv>(
KeyedTraversable<Tk, Tv> $traversable,
)[]: vec<Tk> {
$result = vec[];
foreach ($traversable as $key => $_) {
$result[] = $key;
}
return $result;
}
/**
* Returns a new vec containing an unbiased random sample of up to
* `$sample_size` elements (fewer iff `$sample_size` is larger than the size of
* `$traversable`).
*
* Time complexity: O(n), where n is the size of `$traversable`
* Space complexity: O(n), where n is the size of `$traversable` -- note that n
* may be bigger than `$sample_size`
*/
function sample<Tv>(
Traversable<Tv> $traversable,
int $sample_size,
): vec<Tv> {
invariant(
$sample_size >= 0,
'Expected non-negative sample size, got %d.',
$sample_size,
);
return $traversable
|> shuffle($$)
|> take($$, $sample_size);
}
/**
* Returns a new vec containing the subsequence of the given Traversable
* determined by the offset and length.
*
* If no length is given or it exceeds the upper bound of the Traversable,
* the vec will contain every element after the offset.
*
* - To take only the first `$n` elements, see `Vec\take()`.
* - To drop the first `$n` elements, see `Vec\drop()`.
*
* Time complexity: O(n), where n is the size of the slice
* Space complexity: O(n), where n is the size of the slice
*/
function slice<Tv>(
Container<Tv> $container,
int $offset,
?int $length = null,
)[]: vec<Tv> {
invariant($length === null || $length >= 0, 'Expected non-negative length.');
$offset = _Private\validate_offset_lower_bound($offset, C\count($container));
return cast_clear_legacy_array_mark(\array_slice($container, $offset, $length));
}
/**
* Returns a new vec containing the first `$n` elements of the given
* Traversable.
*
* To drop the first `$n` elements, see `Vec\drop()`.
*
* Time complexity: O(n), where n is `$n`
* Space complexity: O(n), where n is `$n`
*/
function take<Tv>(
Traversable<Tv> $traversable,
int $n,
)[]: vec<Tv> {
if ($n === 0) {
return vec[];
}
invariant($n > 0, 'Expected non-negative N, got %d.', $n);
$result = vec[];
$ii = 0;
foreach ($traversable as $value) {
$result[] = $value;
$ii++;
if ($ii === $n) {
break;
}
}
return $result;
}
/**
* Returns a new vec containing each element of the given Traversable exactly
* once. The Traversable must contain arraykey values, and strict equality will
* be used.
*
* For non-arraykey elements, see `Vec\unique_by()`.
*
* Time complexity: O(n)
* Space complexity: O(n)
*/
function unique<Tv as arraykey>(
Traversable<Tv> $traversable,
)[]: vec<Tv> {
return vec(keyset($traversable));
}
/**
* Returns a new vec containing each element of the given Traversable exactly
* once, where uniqueness is determined by calling the given scalar function on
* the values. In case of duplicate scalar keys, later values will overwrite
* previous ones.
*
* For arraykey elements, see `Vec\unique()`.
*
* Time complexity: O(n * s), where s is the complexity of `$scalar_func`
* Space complexity: O(n)
*/
function unique_by<Tv, Ts as arraykey>(
Traversable<Tv> $traversable,
(function(Tv)[_]: Ts) $scalar_func,
)[ctx $scalar_func]: vec<Tv> {
return vec(Dict\from_values($traversable, $scalar_func));
}