thinkingmedia / underscore

Underscore 是一个 PHP 库,它提供了一些有用的辅助函数,以便在处理数组和集合时使用。

1.0.0 2015-03-19 19:19 UTC

This package is not auto-updated.

Last update: 2024-09-28 16:36:22 UTC


README

Build Status Coverage Status Latest Stable Version Total Downloads Latest Unstable Version License

Underscore 是一个实用库,可以使 PHP 中的数组操作更加愉快。

安装

通过 Composer

$ composer require thinkingmedia/underscore

用法

以下示例假设您已经包含了 Underscore 工具

use Underscore\_;

一些示例可能看起来有点牵强,但实际上非常实用。例如,假设我们有一个虚构的社交网络,并且(出于某种原因)我们想要获取所有通过认证的用户超过 18 岁的二级朋友(朋友的朋友)的姓名。突然,这变得容易多了!

_::create($user->getFriends())->map(function ($f) {
    return $f->getFriends();
})->select(function ($f) {
    return $f->getAge() > 18;
})->pluck('username');

现在(出于更奇怪的原因),我们想知道所有二级朋友(朋友的朋友)超过 18 岁的第三级朋友的数量。

_::create($user->getFriends())->map(function ($f) {
    return $f->getFriends();
})->select(function ($f) {
    return $f->getAge() > 18;
})->reduce(function ($s, $f) {
    return $s + count($f->getFriends());
});

就这些!

注意:在示例中,当返回值注释表明是一个数组时,实际的返回值是一个新的 _ 实例!您可以通过调用 toArray 方法来获取实际的 PHP 数组值。

_ 类实现了 ArrayAccess,因此您可以使用它像使用常规数组一样访问它

$groups = _::create($user->getFriends())->groupBy(function ($friend) {
    $name = $friend->getName();

    return $name[0];
});

$groups['A'] = ...; // All friends with the letter 'A' as the first letter in their name

此外,您还可以使用 foreach 循环遍历容器

$users = _::create(['alice', 1337, 'bob', 42])->chunk(2);

foreach ($users as $name => $karma) {
    // ...
}

all

为容器中的每个元素调用给定的 callback。如果回调返回 false,则方法立即返回 false 并停止枚举。如果所有回调调用都返回 true,则 all 返回 true

_::create([1, 2, 3])->all(function ($n) {
    return $n > 0;
}); // true

any

为容器中的每个元素调用给定的 callback。如果回调返回 true,则方法立即返回 true 并停止枚举。如果所有回调调用都返回 false,则 any 返回 false

_::create([1, 2, 3])->any(function ($n) {
    return $n > 2;
}); // true

chunk

将容器分成一个新数组,该数组由大小为 n 的块组成。

_::create([1, 2, 3, 4])->chunk(2); // [[1, 2], [3, 4]]

combine

将容器与另一个数组合并成键/值对。

_::create([1, 2, 3])->combine(['foo', 'bar', 'baz']); // [1 => 'foo', 2 => 'bar', 3 => 'baz']

concat

返回一个新的数组,它是容器与给定 array 连接到末尾的结果。

_::create([1, 2])->concat([3, 4]); // [1, 2, 3, 4]

dict

将键/值对数组转换为逻辑字典。

_::create([[1, 2], [3, 4]])->dict(); // [1 => 2, 3 => 4]

如果您有一个扁平数组,可以在 dict 之前调用 chunk(2)

_::create([1, 2, 3, 4])->chunk(2)->dict(); // [1 => 2, 3 => 4]

each

为容器中的每个元素调用一次给定的回调,将元素作为参数传递。

_::create([1, 2, 3, 4])->each(function ($n) {
    printf("%d\n", $n);
}); // outputs: 1\n2\n3\n4\n

each 也支持两种和三种参数版本

_::create([1, 2, 3, 4])->each(function ($n, $i) {
    printf("%d: %d\n", $i, $n);
}); //outputs: 0: 1\n1: 2\n2: 3\n3: 4\n

_::create([1, 2, 3, 4]->each(function ($n, $i, $array) {
    // ...
}));

find

将容器中的每个条目传递给给定的回调,返回第一个回调不是 false 的元素。如果没有条目匹配,则返回 null

_::create([1, 2, 3, 4])->find(function ($n) {
    return $n > 2;
}); // 3

first

返回容器中的前 n 个元素。

_::create([1, 2, 3, 4])->first(2); // [1, 2]

flatten

返回一个新的、一维数组,它是容器的递归展平。

_::create([1, [2], [3, [4]]])->flatten(); [1, 2, 3, 4]

提示:如果您只想展平数组的一级,则 flatMap 可能对您很有用!

flatMap

返回一个新的数组,它是调用回调一次为容器中的每个元素生成的结果的连接。

_::create([1, 2, 3, 4])->flatMap(function ($n) {
    return [$n, $n];
}); // [1, 1, 2, 2, 3, 3, 4, 4]

_::create([1, 2, 3, 4])->flatMap(function ($n) {
    return [$n, [$n]];
}); // [1, [1], 2, [2], 3, [3], 4, [4]]

它看起来可能有点愚蠢,但这是一个非常有用的函数,当与其它函数结合使用时!例如,您可以创建(虚构的)用户的字典。

_::create([new User('bob', 32), new User('alice', 35)])->flatMap(function ($u) {
    return [$n->getName(), $n->getAge()];
})->chunk(2)->dict(); // ['bob' => 32, 'alice' => 35]

这最终允许我们作为开发者,在映射数组时创建键/值对!太棒了!

groupBy

按给定回调的结果对容器进行分组。

_::create([1, 2, 3, 4])->groupBy(function ($n) {
    return $n % 2;
}); // [0 => [2, 4], 1 => [1, 3]]

_::create(['foo', 'bar', 'baz'])->groupBy(function ($s) {
    return $s[0];
}); // ['f' => ['foo'], 'b' => ['bar', 'baz']]

has

_::create([1, 2, 3, 4])->has(2); // true
_::create([1, 2, 3, 4])->has(0); // false

indexOf

返回给定对象在容器中的索引,如果元素未找到则返回null

_::create([1, 2, 3, 4])->indexOf(2); // 1
_::create([1, 2, 3, 4])->indexOf(0); // null

inject

通过应用二元运算符组合容器的所有元素。

_::create([1, 2, 3])->inject([], function ($m, $n) {
    $m[$n] = $n * $n;

    return $m;
}); // [1 => 1, 2 => 4, 3 => 9]

_::create(['foo', 'bar', 'baz'])->inject('', function ($m, $s) {
    $m .= $s;
}); // foobarbaz

注意:这是之前提到的唯一例外。这里的返回值是最后一个迭代的结果。

join

返回一个字符串,该字符串将容器的所有元素与提供的分隔符字符串连接起来。

_::create([1, 2, 3, 4])->join('');  // 1234
_::create([1, 2, 3, 4])->join(','); // 1,2,3,4

last

返回容器中的最后n个元素。

_::create([1, 2, 3, 4, 5, 6])->last(2); // [5, 6]

map

对容器中的每个元素执行给定的回调。创建一个新数组,包含块返回的值。

如果给定的回调返回null,则返回数组中跳过该元素。

_::create([1, 2, 3, 4])->map(function ($n) {
    return $n * $n;
}); // [1, 4, 9, 16]

_::create([1, 2, 3, 4])->map(function ($n) {
    return $n % 2 ? $n * $n : null;
}); // [1, 9]

max

返回给定回调返回最大整数的元素。

_::create('1', 'two', 'three')->max(function ($s) {
    return strlen($s);
}); // 'three'

min

返回给定回调返回最小整数的元素。

_::create('1', 'two', 'three')->min(function ($s) {
    return strlen($s);
}); // '1'

none

测试给定回调是否对容器中的每个元素都返回false

_::create([1, 2, 3, 4])->none(function ($n) {
    return $n < 0;
}); // true

_::create([1, 2, 3, 4])->none(function ($n) {
    return $n > 0;
}); // false

partition

根据给定块返回的布尔值将容器分割成两个数组。

_::create(['A', 'B', 'C', 'AA'])->partition(function ($s) {
    return $s[0] == 'A';
}); // [['A', 'AA'], ['B', 'C']]

pluck

返回一个新数组,该数组是通过对容器中的每个元素获取给定属性路径的结果。

_::create([new User('bob'), new User('alice')])->pluck('username'); // ['bob', 'alice']

product

通过假设所有值都可以转换为double值来计算容器的乘积。

_::create([1, 2, 3])->product(); // 6

reduce

将容器减少到单个值。

reduce的常用示例是计算数组中所有值的总和。

_::create([1, 2, 3, 4])->reduce(function ($memo, $n) {
    return $memo + $n;
}); // 10

Reduce还允许在减少数组之前设置一个初始值。

_::create([1, 2, 3, 4])->reduce(function ($s, $n) {
    return $s + $n;
}, 10); // 20

reject

返回一个新数组,包含所有给定的回调返回false的元素。

_::create([1, 2, 3, 4])->reject(function ($n) {
    return ($n % 2) == 0;
}); // [1, 3]

reverse

返回一个新数组,该数组是容器的反转。

_::create([1, 2, 3, 4])->reverse(); // [4, 3, 2, 1]

rotate

返回一个新数组,该数组绕提供的索引旋转。

_::create([1, 2, 3, 4, 5, 6])->rotate(2); // [3, 4, 5, 6, 1, 2]
_::create([1, 2, 3, 4, 5, 6])->rotate(-2); // [5, 6, 1, 2, 3, 4]

sample

从容器中返回一个随机元素。

_::create([1, 2, 3, 4, 5, 6])->sample(); // Basically a dice roll...

select

返回一个新数组,包含所有给定的块返回true的元素。

_::create([1, 2, 3, 4])->select(function ($n) {
    return ($n % 2) == 0;
}); // [2, 4]

shuffle

返回一个新数组,该数组已打乱顺序。

_::create([1, 2])->shuffle(); // Either [1, 2] or [2, 1]

skip

跳过前n个元素并返回其余的数组。

_::create([1, 2, 3, 4, 5, 6])->skip(2); // [3, 4, 5, 6]

slice

返回一个子数组,该子数组从指定的起始索引开始,包含给定数量的元素。

_::create([1, 2, 3, 4])->slice(1, 2); // [2, 3]

snip

截断数组。返回不包含最后n个元素的容器。

_::create([1, 2, 3, 4, 5, 6])->snip(2); // [1, 2, 3, 4]

sort

返回排序后的容器。

_::create([1, 4, 2, 3])->sort(); // [1, 2, 3, 4]

sortBy

使用给定的回调返回值作为排序标准对所有对象进行排序。

$rhombas = new Shape('rhombas');
$ellipse = new Shape('ellipse');
$hexagon = new Shape('hexagon');

_::create([ $rhombas, $ellipse, $hexagon ])->sortBy(function ($s) {
    return $s->getName();
}); // [ $ellipse, $hexagon, $rhombas ]

sum

通过将值转换为double来对对象进行求和。

_::create([1, 2, 3, 4])->sum(); // 10

transpose

假设容器是一个数组数组,并交换行和列。

_::create([[1, 2, 3], [4, 5, 6]])->transpose(); // [[1, 4], [2, 5], [3, 6]]

uniq

通过从容器中删除重复值返回一个新数组。

_::create([1, 2, 3, 1, 2, 4, 1, 2, 5])->uniq(); // [3, 4, 5]

without

返回一个新数组,其中从容器中删除了给定数组中的对象。

_::create([1, 2, 4, 3])->without([4]); // [1, 2, 3]
_::create([1, 2, 3, 4, 5])->without([4, 5]); // [1, 2, 3]

pop

将容器视为栈,并移除最后一个对象,返回它。

_::create()->push(1)->push(2)->push(3)->pop(); // 3

push

将容器视为栈,并将给定的对象添加到容器的末尾。

_::create()->push(1)->push(2)->push(3); // [1, 2, 3]

shift

移除容器中的第一个对象并返回它。

_::create([1, 2, 3])->shift(); // 1

unshift

在容器的开头插入给定的对象,并将容器中的所有其他对象向上移动一个索引。

_::create([2, 3])->unshift(1); // [1, 2, 3]

split

返回一个新数组,该数组由给定字符串中的字符串组成,这些字符串通过给定的分隔符分隔。

_::split('foo bar baz', ' '); // ['foo', 'bar', 'baz']

第二个参数是可选的,默认值为null。如果你传入null或空字符串作为分隔符,你将得到一个包含给定字符串中各个字符的数组。

_::split('1234'); // ['1', '2', '3', '4']

我们可以用这个做很多很酷的事情!

_::split('1234')->sum(); // 10

firstlastskipsnipslice

这些函数之间关系紧密,而且值得记住。

_::create([1, 2, 3, 4, 5])->first(2);       // [1, 2]
_::create([1, 2, 3, 4, 5])->last(2);        // [4, 5]
_::create([1, 2, 3, 4, 5])->skip(2);        // [3, 4, 5]
_::create([1, 2, 3, 4, 5])->snip(2);        // [1, 2, 3]
_::create([1, 2, 3, 4, 5])->slice(2, 2);    // [3, 4]

贡献

请参阅贡献指南

鸣谢

本项目深受YOLOKit的启发。对于那些使用Objective-C进行开发的开发者,我强烈推荐你们去看看!

许可证

请参阅许可证