2016-03-11 3 views
2

Я ищу взять коллекцию и вернуть плоские массивы, содержащие два поля из коллекции. То, что я хочу:Коллекция Laravel - список несколько значений

$collection = new Collection([ 
    [ 
     "a"=>"a1", 
     "b"=>"b1", 
     "c"=>"c1" 
    ], 
    [ 
     "a"=>"a2", 
     "b"=>"b2", 
     "c"=>"c2" 
    ], 
    [ 
     "a"=>"a3", 
     "b"=>"b3", 
     "c"=>"c3" 
    ] 
]); 

//desired output: 
[ 
    ["a1","b1"], 
    ["a2","b2"], 
    ["a3","b3"] 
]; 

Я бы подумал, что это функция, но это не так:

$collection->lists(["a","b"]); 

Мое текущее решение не кажется очень эффективным при условии, что они могут иметь много тысяч строки:

$collection->lists("a")->zip($collection->lists("b")); 

Есть функция, которой я не хватает? Благодарю.

Edit: Я также использовал карту так:

$collection->map(function($row)){ 
    return [$row['a'],$row['b']]; 
} 

Это кажется немного больше обычай, чем я хочу. Я может в конечном итоге писать обертку для коллекции с помощью этой функции, если не существует собственный способ ..

ответ

9

Если вы хотите, чтобы специально использовать коллекции вы можете расширить текущий Collections API, как это macroable, делать что-то вроде следующего:

Collection::macro('pluckMultiple', function ($assoc) { 
    return $this->map(function ($item) use ($assoc) { 
     $list = []; 
     foreach ($assoc as $key) { 
      $list[$key] = data_get($item, $key); 
     } 
     return $list; 
    }, new static); 
}); 

В приведенном выше примере я включаю ключ в качестве части возврата, но это можно отбросить.

$collection->pluckMultiple(['a', 'b']); 

даст вам то, что вы ищете.

выше может работать хорошо, если вы хотите, чтобы срывать предметы из красноречивых коллекций, например:

$books = Book::with('author')->get(); 
$books->pluckMultiple(['title', 'author.name', 'author.surname']); 
+0

Это отличное решение. Я был фактически брошен этим и удивлен, увидев, что метод pluck не принимает это по умолчанию. Похоже, мне это нужно. – jiraiya

+0

В каком файле следует добавить это письмо? – Ken

+1

@ Ken вы можете сделать это разными способами. Если у вас не так много макросов, вы можете поместить их в метод 'register' вашего' AppServiceProvider'. В качестве альтернативы вы можете проверить следующие [темы форума] (https://laracasts.com/discuss/channels/general-discussion/best-practices-for-custom-helpers-on-laravel-5) для альтернатив, сгруппировать их в файл и использование автозагрузки. –

5

Вы можете использовать

$plucked = $collection->pluck('b','a'); 

$plucked->all(); 

это даст следующий результат

Collection {#651 ▼ 
#items: array:3 [▼ 
"a1" => "b1" 
"a2" => "b2" 
"a3" => "b3" 
]} 

и yuse этот код для получения данного формата

foreach ($plucked as $key => $item) 
{ 
$temp[] = [ $key, $item]; 
} 

это даст следующий результат.

array:3 [▼ 
0 => array:2 [▼ 
    0 => "a1" 
    1 => "b1" 
    ] 
1 => array:2 [▼ 
    0 => "a2" 
    1 => "b2" 
    ] 
2 => array:2 [▼ 
    0 => "a3" 
    1 => "b3" 
] 
] 
+0

Я хотел бы, чтобы не идти через массив более чем один раз. Я знаю, что могу перебирать один раз с помощью 'map()', но кажется, что должен быть лучший способ. – Jeff

+0

. Я не думаю, что есть какая-либо прямая функция, которая даст желаемый результат. – nextt1

+0

Спасибо. Примите ваш ответ, если ничего не появится. – Jeff

-1

Я изменил Leon Vismer ответ использовать точечную нотацию в массиве.

 Collection::macro('pluckMultiple', function ($assoc) { 
     /* @var $this Collection */ 
     return $this->map(function ($item) use ($assoc) { 
      $list = []; 
      foreach ($assoc as $keys) { 
       $old_key = $keys; 
       $keys = explode('.', $keys); 
       $ref  = &$list; 

       while (count($keys) > 1) { 
        $key = array_shift($keys); 
        if (!isset($ref[ $key ]) || !is_array($ref[ $key ])) { 
         $ref[ $key ] = []; 
        } 

        $ref = &$list[ $key ]; 
       } 

       $ref[ array_shift($keys) ] = data_get($item, $old_key); 
      } 

      return $list; 
     }, new static); 
    }); 
+0

data_get() уже использует точечную нотацию – Vedmant

+0

Нет, выходные данные выводятся с точками. тогда как мой код преобразует точки в массив. Пример: { customer.name => 'Раз' customer.dob => '1985-01-01' } к { клиента { имени => 'Раз' д.р. => «1985- 01-01 ' } – Raza

0

Если вы используете Laravel 5.0 вы обнаружите, что Illuminate\Laravel\Collection не принимает макросы, поэтому элегантное решение Леона не будет работать. Если (как я), вы не можете uprade до новой версии Laravel, вы можете использовать эту функцию в качестве обходного пути:

<?php 

use Illuminate\Support\Collection; 

/** 
* List values from collection. Unlike $collection->lists('key'), this function allows using multiple keys. 
* 
* In newer Laravel versions things like that can be done with collection macros, but not in 5.0. 
* 
* @param Collection $collection 
* @param array|string $properties 
* @param string $id 
* @return array 
*/ 
function lists(Collection $collection, $properties, $id = null) { 
    $result = []; 
    $i = 0; 

    foreach ($collection as $item) { 
     $k = $id ? $item->$id : $i; 
     if (is_array($properties)) { 
      $result[$k] = []; 
      foreach ($properties as $property) { 
       $result[$k][$property] = $item->$property; 
      } 
     } 
     else { 
      $result[$k] = $item->$properties; 
     } 
     $i++; 
    } 

    return $result; 
} 

Тогда вы бы использовать его, например, как это:

$users = User::all(); 
$someData = lists($collection, ['name', 'email']); 
// Or, if you want to key by id: 
$someData = lists($collection, ['name', 'email'], 'id'); 

Что касается того, где вы положили эту функцию, я добавляю такие вещи в app/Support/helpers.php, а затем обновляю свой композитор.JSON как это:

"autoload": { 
    ... 
    "files": [ 
     "app/Support/helpers.php" 
    ] 
}, 

, а затем запустить composer dump

Смежные вопросы