Given following collection/array:
[
"somename" => "test.test.be"
"anothername" => "test"
"yetanothername" => "testing"
"extrafield" => "extra",
"extrafield" => "extra",
]
When i retrieve this collection i always know the order of them, but i will not know the key-names. So what i want to do is transform this collection and change the keynames to my defined values.
For a non-associative array i would do something like
$trimmedCollection->transform(function ($item) {
return [
'email' => $item[0],
'first_name' => $item[1],
'surname' => $item[2],
];
});
But how would i handle this for the given collection? Also what to do with overflow items. Say i suddenly got 10 key-value pairs but only wrote a transform for 3 how would i transform all the overflow to a default key?
Edit:
For the overflow items i would like to assign all extra fields in the given array to be stored like so.
Below would be the final array:
[
"email" => "test.test.be"
"first_name" => "test"
"surname" => "testing"
"additional_fields" => ["key-name" => "extra","key-name" => "extra"]
]
Where the key-name is the original name of the key i retrieved.
You can use array_shift to remove the 1st element in the array for every known element, and add the remaining array to your additional_fields key:
$trimmedCollection->transform(function ($item) {
return [
'email' => array_shift($item), //$item[0]
'first_name' => array_shift($item), //$item[1]
'surname' => array_shift($item), //$item[2]
'additional_fields' => $item //all remaining items
];
});
You could do something like this to transform your selected keys. This retains the other values with their unchanged keys.
function replace_array_key(array &$item, $oldKey, $newKey)
{
$item[$newKey] = $item[$oldKey];
unset($item[$oldKey]);
}
$trimmedCollection->transform(function ($item) {
replace_array_key($item, 'somename', 'email');
replace_array_key($item, 'anothername', 'first_name');
replace_array_key($item, 'yetanothername', 'surname');
return $item;
});
You can even extend this to an array list of old and new key names and run it through and arrap_map.
Related
I'm looking for a smart way to find out if my array of objects within an object has multiple name values or not to do a validation since it's only allowed to have one array name per inner array:
$elements = [];
$elements[18][20] = [
[
'name' => 'Color',
'value' => 'Red'
],
[
'name' => 'Color',
'value' => 'Green'
],
[
'name' => 'Size',
'value' => 'S'
]
];
$elements[18][21] = [
[
'name' => 'Size',
'value' => 'S'
],
[
'name' => 'Length',
'value' => '20'
],
];
error_log( print_r( $elements, true ) );
So the object 20 for example is invalid because it has 2 colors with the same value. At the end I was hoping to get a result array containing 1 duplicate name. This way I can output them like: "You have at least one duplicate name: Color".
My first idea was to loop over the array and do a second loop. This way it was possible to receive the inner arrays containing the stuff. Now I was able to add every name to another array. After this I was able to use count() and array_intersect() to receive a value of x which showed me if there are duplicates or not.
Now I had a count, but not the actual value to display. Before I use a semi-good solution, I was hoping to get any ideas here how I can make it better!
This loop will generate your expected output:
foreach($elements[18] as $index => $element){
//Get all the elements' names
$column_key = array_column($element, 'name');
//Get the count of all keys in the array
$counted_values = array_count_values($column_key);
//Check if count is > 1
$filtered_array = array_filter($counted_values, fn($i) => $i > 1);
//If the filter is not empty, show the error
if(!empty($filtered_array)){
//get the key name
$repeated_key = array_key_first($filtered_array);
echo "You have at least one duplicate name: {$repeated_key} at index {$index}";
break;
}
}
It relies in the array_count_values function.
In my Laravel project, I have a dot notation array which I need to convert to a multi-dimensional array.
The array is something like this:
$dotNotationArray = ['cart.item1.id' => 15421a4,
'cart.item1.price' => '145',
'cart.item2.id' => 14521a1,
'cart.item2.price' => '1245'];
How can I expand it to an array like:
'cart' => [
'item1' => [
'id' => '15421a4',
'price' => 145
],
'item2' => [
'id' => '14521a1',
'price' => 1245,
]
]
How can I do this?
In Laravel 6+ you can use Arr::set() for this:
The Arr::set method sets a value within a deeply nested array using "dot" notation:
use Illuminate\Support\Arr;
$multiDimensionalArray = [];
foreach ($dotNotationArray as $key => $value) {
Arr::set($multiDimensionalArray , $key, $value);
}
dump($multiDimensionalArray);
If you are using Laravel 5.x you can use the array_set() instead, which is functionally identical.
Explanation:
Arr::set() sets value for a key in dot notation format to a specified key and outputs an array like ['products' => ['desk' => ['price' => 200]]]. so you can loop over your array keys to get a multidimensional array.
I am trying to take two arrays, and merge them to each other. The first array serves as an 'index' array, that is - that is the format that the output arrays desirably would be:
$array1 = [
'DIV1' => 'Some element data',
'SUPPLEMENTAL' => [
'RPC' => '10.24.122.32',
'PORT' => '8080'
],
'ASG' => 'some arbitrary data'
];
$array2 = [
'DIV2' => 'Some more element data',
'ASG' => 'different arbitrary data',
'DIV1' => 'Some element data that refers to the other object'
'SUPPLEMENTAL' => [
'RPC' => '10.24.123.1'
]
];
So after the merge, we would effectively have two arrays. This can be done as a a single function called twice, which passes each array as parameters (reversed for the second call - and somehow defining the index array). The keys would be carried over -only-, no values. We would end up with arrays looking like this:
$array1 = [
'DIV1' => 'Some element data',
'DIV2' => '', // blank because only key was moved
'SUPPLEMENTAL' => [
'RPC' => '10.24.122.32',
'PORT' => '8080'
],
'ASG' => 'some arbitrary data'
];
$array2 = [
'DIV1' => 'Some element data that refers to the other object'
'DIV2' => 'Some more element data',
'SUPPLEMENTAL' => [
'RPC' => '10.24.123.1',
'PORT' => '' // blank because only key was moved
],
'ASG' => 'different arbitrary data'
];
It is not -extremely- important that the imported (blank) keys are put in some order, but the preservation of order of existing elements is important. As long as it abides by the index arrays order definition (array1 in this case).
I think I would need to do some sort of nested sort for the multiple dimensions.
Because your data doesn't have keys in the same order it'll be difficult to maintain key order, but you can achieve what you need with a recursive function:
function recursiveReKeyArrays(array $array1, array $array2)
{
// Loop through the array for recursion
foreach ($array2 as $key => $value) {
if (!is_array($value)) {
continue;
}
$array1[$key] = recursiveReKeyArrays($array1[$key], $value);
}
// Find the differences in the keys
foreach (array_diff_key($array2, $array1) as $key => $value) {
$array1[$key] = null;
}
return $array1;
}
This will loop through the second array, find any values which are arrays and recurse into them and find any missing keys and set them to null.
This will give you this output:
Array
(
[DIV1] => Some element data
[SUPPLEMENTAL] => Array
(
[RPC] => 10.24.122.32
[PORT] => 8080
)
[ASG] => some arbitrary data
[DIV2] =>
)
Array
(
[DIV2] => Some more element data
[ASG] => different arbitrary data
[DIV1] => Some element data that refers to the other object
[SUPPLEMENTAL] => Array
(
[RPC] => 10.24.123.1
[PORT] =>
)
)
Example here: http://ideone.com/5ml1y4
I am using Laravel 5.3.
existing Collection $a is
$a = collect(
[
0 =>[
'firstName' => 'John',
'lastName' => 'Doe'
],
1 =>[
'firstName' => 'Mary',
'lastName' => 'Jane'
]
]);
and desired result is as below:
$a = collect(
[
0 => [
'firstName' => 'John',
'lastName' => 'Doe',
'occupation' => 'engineer'
],
1 => [
'firstName' => 'Mary',
'lastName' => 'Jane',
'occupation' => 'accountant'
]
]);
I tried to test ->push(), ->put(), ->prepend() but no success. Please let me know the best way to do this.
You can use transform method which iterates over the collection and calls the given callback with each item in the collection. The items in the collection will be replaced by the values returned by the callback:
$a->transform(function ($item, $key) {
$item['occupation'] = 'some_value';
retrun $item;
});
Then check the value of $a:
dd($a);
Note that you have a collection of arrays, not a collection of collections. Hence, you can't use push(), put(), etc., in the sub-arrays.
The easiest way is probably just to treat it as an array:
$a[0]['occupation'] = 'engineer';
But you can also access at least the first element through the getter if you'd like:
$a->get(0)['occupation'] = 'engineer';
If you have an existing collection then you can get all the arrays from it using something like this:
$items = $existingCollection->all();
Now, you have an array of arrays in $items. So you can add key/value using something like this:
$item[0]['occupation'] = 'engineer';
$item[1]['occupation'] = 'accountant';
Now, you've modified the arrays so if you want to turn it back into a collection then you can do it easily using this:
$existingCollection = collect($items);
You could've also used map on the collection directly, for example:
$existingCollection->map(function ($person) {
$person['occupation'] = 'engineer';
return $person;
});
In this case, you can see that, all the person's occupation is going to be same but you can find out a way to make the difference but I can't give you more precise solution because I don't know much about your situation (You didn't share much).
Why is this array count returning 1 instead of 2?
Shouldn't it return 2?
$join = [
'JOIN' => ['coins','users.id','user_id'],
'JOIN' => ['coins','users.id','user_id']
];
echo count($join);
You are creating an associative array, which means that each element is associated to one unique key. Therefore, in an array, each key can only appear once. A key appearing twice means that the value will be overwritten.
If you try to var_dump your array, it would have this output:
array(1) {
["JOIN"]=>
array(3) {
[0]=>
string(5) "coins"
[1]=>
string(8) "users.id"
[2]=>
string(7) "user_id"
}
}
As seen from this result, only one line exists.
If you need to have 'JOIN' in every element, maybe you want to change your array structure into this:
$join = [
['JOIN' => ['coins','users.id','user_id']],
['JOIN' => ['coins','users.id','user_id']]
];
This will carry the information 'JOIN' in every element. However, I cannot imagine why you would need such a thing.
Instead, maybe you want to have multiple elements under the 'JOIN key:
$join = [
'JOIN' => [
['coins','users.id','user_id'],
['coins','users.id','user_id']
]
];
As per your comments, maybe you eventually want to have a structure like this:
$join = [
'JOIN' => [
['coins','users.id','user_id'],
['coins','users.id','user_id'],
],
'INNER JOIN' => [
['coins','users.id','user_id'],
['coins','users.id','user_id'],
]
];
According to your comments, it might be more desirable if you do this through object-oriented programming instead:
class Join{
const JOIN = 0;
const INNER_JOIN = 1;
// we are using constants to prevent bugs caused by typos
public $type;
public $coins;
public $usersDotId; // I don't really know what you are trying to do here
public $userId;
}
Then you can use it like this:
$joins = [];
$join = new Join();
$join->type = Join::INNER_JOIN;
$join->coins = "some_value";
$join->usersDotId = "some_value";
$join->userId = "some_value";
$joins[] = $id;
$join = [
'JOIN' => ['coins','users.id','user_id'],
'JOIN' => ['coins','users.id','user_id']
];
echo count($join);
how may I modify this array to have 2 keys, without changing the key name?
In order to not need to change this key name, make JOIN an array of numeric values so structurally you want:
array:
----> [Join] :
\---> [0]
\---> coins
|---> users.id
|---> user.id
This can be achieved with this syntax (clarified for understanding):
$join = [
'JOIN' => [0] => ['coins','users.id','user_id'],
[1] => ['coins','users.id','user_id']
];
(simplified for ease):
$join = [
'JOIN' => ['coins','users.id','user_id'],
['coins','users.id','user_id']
];
gives you a $join array with a count of 1, which contains (the one) an array with a count of 2, each of those containing 3 elements.
Without addressing the more specific problem that appeared in the comments, and acknowledging that you've already accepted an answer that works for your purposes, here is a more theoretical explanation for why you can't have two of the same key. This may be not be useful for you, but hopefully it could help someone who does not intuitively grasp this concept.
Regardless of whether you assign your own string keys ($array = ['key' => 'value'];), assign your own integer keys ($array = [42 => 'the answer'];), or let PHP automatically assign integer keys ($array[] = 'something';), the keys must be unique.
According to the PHP manual for arrays:
An array in PHP is actually an ordered map. A map is a type that associates values to keys.
Most programming languages have something like this. This association of values to keys means that by definition, keys must be unique. If a language allowed you to create a "map" with multiple identical keys pointing to different values, looking up a value by key would be impossible (or at least would produce ambiguous results), so the map would be fairly useless.
PHP will let you write things like:
$example = [
'a' => 1,
'b' => 2,
'a' => 3,
];
var_dump($example);
without giving any errors, but the result of that will be:
array (size=2)
'a' => int 3
'b' => int 2
where 'a' => 3 has overwritten the previously defined value of the 'a' key. This allows the map to continue to work, so that $example['a'] will always yield the same value. If you actually could have
array (size=3)
'a' => int 1
'b' => int 2
'a' => int 3
Then what would be the result of $example['a']? Any defined behavior (first instance of 'a', etc.) would mean that some values of 'a' would be inaccessible.
Because you are using same array key to both the element
try this,
<?php
$join = [
'JOIN1' => ['coins','users.id','user_id'],
'JOIN2' => ['coins','users.id','user_id']
];
echo count($join);
?>
Or with same key try following
$join = [
['JOIN' => ['coins','users.id','user_id']],
['JOIN' => ['coins','users.id','user_id']]
];