Skip to content

[5.8] Add join method to collection - #27723

Merged
taylorotwell merged 2 commits into
laravel:5.8from
freekmurze:5.8
Mar 7, 2019
Merged

taylorotwell merged 2 commits into
laravel:5.8from
freekmurze:5.8

Conversation

@freekmurze

Copy link
Copy Markdown
Contributor

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

Copy link
Copy Markdown
Contributor

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.

Comment thread src/Illuminate/Support/Collection.php Outdated
return '';
}

if ($this->count() === 1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you cache the result of count method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, done.

}

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this be cast to string explicitly?

@CupOfTea696

CupOfTea696 commented Mar 1, 2019 •

Copy link
Copy Markdown
Contributor

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

fisharebest commented Mar 3, 2019 •

Copy link
Copy Markdown
Contributor

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 = '')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe rename finalGlue to lastGlue?

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

@tontonsb

tontonsb commented Mar 5, 2019 •

Copy link
Copy Markdown
Contributor

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants