Skip to content

head/first aligned with lodash/underscore #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "lodash-php/lodash-php",
"name": "sfinktah/lodash-php",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the changes in this file should be reverted

"description": "A port of Lodash to PHP",
"keywords": [
"lodash",
Expand All @@ -24,13 +24,12 @@
},
"require": {
"php": ">=7.1",
"sebastian/comparator": "^1.2 | ^2.0 | ^2.1 | ^3.0 | ^4.0",
"symfony/property-access": "^2.7 | ^3.0 | ^4.0 | ^5.0"
"sebastian/comparator": "*",
"symfony/property-access": "*"
},
"require-dev": {
"phpdocumentor/reflection-docblock": "^4.2",
"bamarni/composer-bin-plugin": "^1.2",
"phpstan/phpdoc-parser": "^0.3.0"
"bamarni/composer-bin-plugin": "*",
"phpunit/phpunit": "^10"
},
"scripts": {
"post-install-cmd": ["@composer bin all install --ansi"],
Expand Down
50 changes: 50 additions & 0 deletions src/Array/firstOr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

/*
* This file is part of the SolidWorx Lodash-PHP project.
*
* @author Pierre du Plessis <open-source@solidworx.co>
* @copyright Copyright (c) 2017
*/

namespace _;

/**
* Gets the first element of `array` or `default` if the array is empty
*
* @alias firstOr
* @param mixed $array The array to query.
* @param mixed|callable $default Value or Callable if array is empty *
*
* @return mixed Returns the first element of `array` or `default` if empty.
*
* @category Array
*
* @example
* <code>
* head([1, 2, 3])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example should reference headOr

* // => 1
*
* head([])
* // => null
* </code>
*/
function headOr(mixed $array, mixed $default)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mixed type is only available since PHP 8, while this library still supports PHP 7.x

{
if ((is_array($array) || $array instanceof \ArrayObject) && count($array)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of this logic can go to the head function, then this can be simplified to the following:

function headOr($array, $default)
{
    return head($array) ?? (is_callable($default) ? $default($array) : $default);
}

return reset($array);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling reset() on an ArrayObject does not work since PHP 7.4, so this needs to be handled separately.

Copy link
Author

@sfinktah sfinktah Mar 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your many comments are appreciated and inarguably valid. I will not waste time responding to the others, but this one is quite interesting.

Personally, I have always disliked using reset() to get the head of an array. Coming from C++ it feels like mutating a const ref. I realise array is passed by value, so no mutation is occuring, though ArrayObject would be by reference.

I would much rather write

function head($array) {
    foreach ($array as $value) {
        return $value;
    }
    return null;
}

function headOr($array, $default) {
    head($array) ?? (is_callable($default) ? $default($array) : $default);
}

As you suggested in another comment, headOr really doesn't need to be overly complex.

That does upset my OCD a little though:

$array = [null, 1, 2];
printf("First: %s\n", head($array, "Empty Array"));

Would return "Empty Array", would it not?

}
else if (is_string($array) && strlen($array)) {
return substr($array, 0, 1);
}
return is_callable($default) ? $default($array) : $default;
}

/* alias to head() */
function firstOr(mixed $array, mixed $default)
{
return headOr($array, $default);
}

40 changes: 34 additions & 6 deletions src/Array/head.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* @alias first
* @category Array
*
* @param array $array The array to query.
* @param mixed $array The array to query.
*
* @return mixed Returns the first element of `array`.
*
Expand All @@ -30,15 +30,43 @@
* // => null
* </code>
*/
function head(array $array)
function head(mixed $array)
{
reset($array);

return current($array) ?: null;
if ((is_array($array) || $array instanceof \ArrayObject) && count($array)) {
return reset($array);
}
else if (is_string($array) && strlen($array)) {
return substr($array, 0, 1);
}
return null;
}

/* alias to head() */
function first(array $array)
function first(mixed $array)
{
return head($array);
}

$COMMENT = <<<JSON
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can move to the docblock of the head function

# lodash (4.17.15) (JavaScript)
```js
function head(array) {
return (array && array.length) ? array[0] : undefined;
}
```
> a = [ [], [1], [1, 2], "123", "1", null, false, "" ];
> b = _.map(a, function(v) { return _.first(v); });
> JSON.stringify({a:a, b:b});
{"a":[[],[1],[1,2],"123","1",null,false,""],"b":[null,1,1,"1","1",null,null,null]}


# Underscore.js (1.13.6) (JavaScript)
{"a":[[],[1],[1,2],"123","1",null,false,""],"b":[null,1,1,"1","1",null,null,null]}
```js
export default function first(array, n, guard) {
if (array == null || array.length < 1) return n == null || guard ? void 0 : [];
if (n == null || guard) return array[0];
return initial(array, array.length - n);
}
```
JSON;
55 changes: 55 additions & 0 deletions src/Lang/castArray.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

/*
* This file is part of the SolidWorx Lodash-PHP project.
*
* @author Pierre du Plessis <open-source@solidworx.co>
* @copyright Copyright (c) 2017
*/

namespace _;

/**
* Casts `value` as an array if it's not one.
*
* @param mixed $value The value to cast
*
* @returns {Array} Returns the cast array.
* @param array $array The array to concatenate.
*
* @return array Returns the cast array.
*
* @example
*
* _.castArray(1);
* // => [1]
*
* _.castArray({ 'a': 1 });
* // => [{ 'a': 1 }]
*
* _.castArray('abc');
* // => ['abc']
*
* _.castArray(null);
* // => [null]
*
* _.castArray(undefined);
* // => [undefined]
*
* _.castArray();
* // => []
*
* var array = [1, 2, 3];
* console.log(_.castArray(array) === array);
* // => true
*/
function castArray($value): array
{
$check = function ($value): array {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to the following:

function castArray($value): array
{
    return (array) $value;
}

return \is_array($value) ? $value : [$value];
};

return $check($value);
}
44 changes: 44 additions & 0 deletions src/Lang/isArrayLike.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

/*
* This file is part of the SolidWorx Lodash-PHP project.
*
* @author Pierre du Plessis <open-source@solidworx.co>
* @copyright Copyright (c) 2017
*/

namespace _;

use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory;

/**
* Check if the value is an Array and that the keys are entirely numeric (integer)
*
* @category Lang
*
* @param mixed $value The value to check.
*
* @return bool Returns `true` if the value is an array, else `false`.
*
* @example
* <code>
*
* $object = [ 'a' => 1]
* $array = [ 'a' ]
*
* isArrayLike($array)
* // => true
*
* isArrayLike($object)
* // => false
*
* </code>
*/
function isArrayLike(mixed $value): bool {
return is_array($value) && every(array_keys($value), function ($key) {
return is_int($key);
});
}
44 changes: 44 additions & 0 deletions src/Lang/isObjectLike.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

/*
* This file is part of the SolidWorx Lodash-PHP project.
*
* @author Pierre du Plessis <open-source@solidworx.co>
* @copyright Copyright (c) 2017
*/

namespace _;

use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory;

/**
* Check if the value is an Array and that not all keys are entirely numeric (integer)
*
* @category Lang
*
* @param mixed $value The value to check.
*
* @return bool Returns `true` if the value is an object-like array, else `false`.
*
* @example
* <code>
*
* $object = [ 'a' => 1]
* $array = [ 'a' ]
*
* isObjectLike($array)
* // => false
*
* isObjectLike($object)
* // => true
*
* </code>
*/
function isObjectLike(mixed $value): bool {
return $value instanceof \stdClass || is_array($value) && !every(array_keys($value), function ($key) {
return is_int($key);
});
}
54 changes: 54 additions & 0 deletions src/Object/assign.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

/*
* This file is part of the SolidWorx Lodash-PHP project.
*
* @author Pierre du Plessis <open-source@solidworx.co>
* @copyright Copyright (c) 2018
*/

namespace _;

use function _\internal\basePick;
use function _\internal\flatRest;

/**
* Assigns own enumerable string keyed properties of source objects to the
* destination object. Source objects are applied from left to right.
* Subsequent sources overwrite property assignments of previous sources.
*
* **Note:** This method mutates `object` and is loosely based on
* [`Object.assign`](https://mdn.io/Object/assign).
*
* @param object $object The destination object.
* @param object ...$sources The source objects.
* @return object Returns the new object.
* @category Object
* @since 0.10.0
* @example
*
* function Foo() {
* this.a = 1;
* }
*
* function Bar() {
* this.c = 3;
* }
*
* Foo.prototype.b = 2;
* Bar.prototype.d = 4;
*
* _.assign({ 'a': 0 }, new Foo, new Bar);
* // => { 'a': 1, 'c': 3 }
*/
function assign(object $object, object ...$sources): \stdClass
{
foreach ($sources as $source) {
foreach ($source as $key => $value) {
$object->$key = $value;
}
}
return $object;
}
45 changes: 45 additions & 0 deletions src/Object/omit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

/*
* This file is part of the SolidWorx Lodash-PHP project.
*
* @author Pierre du Plessis <open-source@solidworx.co>
* @copyright Copyright (c) 2018
*/

namespace _;

use function _\internal\basePick;
use function _\internal\flatRest;

/**
* The opposite of `_.pick`; this method creates an object composed of the
* own and inherited enumerable property paths of `object` that are not omitted.
*
* **Note:** This method is considerably slower than `_.pick`.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param array $object The source object.
* @returns array Returns the new object.
* @example
*
* var object = { 'a': 1, 'b': '2', 'c': 3 };
*
* _.omit(object, ['a', 'c']);
* // => { 'b': '2' }
*/
function omit(array $object, array $paths): array
{
$result = [];
foreach ($object as $key => $value) {
if (!in_array($key, $paths)) {
$result[$key] = $value;
}
}
return $result;
}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy