Skip to content
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

[5.8] Add `join` method to collection #27723

Merged
merged 2 commits into from Mar 7, 2019

Conversation

Projects
None yet
8 participants
@freekmurze
Copy link
Member

freekmurze commented Feb 28, 2019

This PR adds a function to easily use a separate "glue" for the final item.

collect(['a', 'b', 'c']))->join(', ')); // returns 'a, b, c'
 
collect(['a', 'b', 'c']))->join(', ', ' and ')); // returns 'a, b and c'
 
collect(['a', 'b']))->join(', ', ' and ')); // returns 'a and b'
 
collect(['a']))->join(', ', ' and ')); // returns 'a'
 
collect([]))->join(', ', ' and ')); // returns ''

@freekmurze freekmurze changed the title [5.8 ] Add `join` method to collection [5.8] Add `join` method to collection Feb 28, 2019

@fitztrev

This comment has been minimized.

Copy link
Contributor

fitztrev commented Feb 28, 2019

I'll recap from #27657

  1. PHP join() and implode() are aliases of one another but Collection::join() and Collection::implode() are 2 similar yet different methods. Could be confusing.
  2. Despite personal or regional preferences, it would be nice to support Oxford commas.

For reference, times when Oxford commas have been added in Laravel, and Rails to_sentence which allows the developer the option.

return '';
}
if ($this->count() === 1) {

This comment has been minimized.

@bonzai

bonzai Feb 28, 2019

Contributor

Can you cache the result of count method?

This comment has been minimized.

@freekmurze

freekmurze Mar 1, 2019

Author Member

Makes sense, done.

}
if ($this->count() === 1) {
return $this->last();

This comment has been minimized.

@CupOfTea696

CupOfTea696 Mar 1, 2019

Contributor

Can this be cast to string explicitly?

@CupOfTea696

This comment has been minimized.

Copy link
Contributor

CupOfTea696 commented Mar 1, 2019

Additionally, as noted in the previous PR for this, can we add support for the Oxford comma?

There are two ways I can think to implement this.

  • Add a third parameter that will be used as glue if there are exactly 2 items in the collection.
  • Add a third boolean parameter, that automatically removes a leading comma from the glue if there are exactly two items in the collection.

Desired result:

collect(['a', 'b', 'c']))->join(', ', ', and ')); // returns 'a, b, and c'
 
collect(['a', 'b']))->join(', ', ', and ', ' and ')); // returns 'a and b'

collect(['a', 'b']))->join(', ', ', and ', true)); // returns 'a and b'

Implementation 1:

    /**
     * Join all items from the collection using a string. The final items can use a separate glue string.
     *
     * @param  string  $glue
     * @param  string  $finalGlue
     * @param  string  $dyadicGlue
     * @return string
     */
    public function join($glue, $finalGlue = null, $dyadicGlue = null)
    {
        if (func_num_args() === 1) {
            return $this->implode($glue);
        }

        $count = $this->count();

        if ($count < 2) {
            return $this->implode('');
        }

        if ($count === 2) {
            return $this->implode(func_num_args() === 3 ? $dyadicGlue : $finalGlue)
        }

        $collection = new static($this->items);

        $finalItem = $collection->pop();

        return $collection->implode($glue).$finalGlue.$finalItem;
    }

Implementation 2:

    /**
     * Join all items from the collection using a string. The final items can use a separate glue string.
     *
     * @param  string  $glue
     * @param  string  $finalGlue
     * @param  bool  $oxfordComma
     * @return string
     */
    public function join($glue, $finalGlue = null, $oxfordComma = false)
    {
        if (func_num_args() === 1) {
            return $this->implode($glue);
        }

        $count = $this->count();

        if ($count < 2) {
            return $this->implode('');
        }

        if ($count === 2 && $oxfordComma) {
            $finalGlue = preg_replace('/^,/', '', $finalGlue);
        }

        $collection = new static($this->items);

        $finalItem = $collection->pop();

        return $collection->implode($glue).$finalGlue.$finalItem;
    }
@fisharebest

This comment has been minimized.

Copy link
Contributor

fisharebest commented Mar 3, 2019

There are two ways I can think to implement this.

  • Add a third parameter that will be used as glue if there are exactly 2 items in the collection.
  • Add a third boolean parameter, that automatically removes a leading comma from the glue if there are exactly two items in the collection.

Please use the first option, rather than the second. When dealing with I18N, there are always "awkward" cases, and logic such as "automatically removing one string from another" cannot be guaranteed to work.

A simple example might be where the items are a mix of LTR and RTL text, and are being displayed on an LTR page. Your joiners might be <span dir="ltr">, </span> and <span dir="ltr"> and </span>

* @param string $finalGlue
* @return string
*/
public function join($glue, $finalGlue = '')

This comment has been minimized.

@olivernybroe

olivernybroe Mar 4, 2019

Contributor

Maybe rename finalGlue to lastGlue?

*/
public function join($glue, $finalGlue = '')
{
if ($finalGlue === '') {

This comment has been minimized.

@olivernybroe

olivernybroe Mar 4, 2019

Contributor

Should we change the default value to null instead of '', as this removes the option for having an empty string as your finalGlue.

@tontonsb

This comment has been minimized.

Copy link
Contributor

tontonsb commented Mar 5, 2019

I think this really misses the ability to pluck at the same time. Ability to include a readable $winners->implode('name', ', ') is why I really enjoy the implode method.

Don't you think this functionality would be more appropriate as an option for implode? As it stands right now, the both methods would seem half useful. Why not make $winners->implode('name', ', ', ' and last but not least ', ' and ') happen?

@taylorotwell taylorotwell merged commit 3fe2187 into laravel:5.8 Mar 7, 2019

2 checks passed

continuous-integration/styleci/pr The analysis has passed
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

chapeupreto added a commit to chapeupreto/docs that referenced this pull request Mar 13, 2019

add documentation to `join` method on collection
This PR adds documentation to the `join` method: laravel/framework#27723
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.