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
Merged

[5.8] Add join method to collection #27723

merged 2 commits into from
Mar 7, 2019

Conversation

freekmurze
Copy link
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
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.

return '';
}

if ($this->count() === 1) {
Copy link
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
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
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
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
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 = '')
Copy link
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
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
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?

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