Searching an array for an object's member variable - php

How would something like this be possible:
I have an object called Player:
class Player
{
public $name;
public $lvl;
}
and I have an array of these players in: $array.
For example $array[4]->name = 'Bob';
I want to search $array for a player named "Bob".
Without knowing the array key, how would I search $array for a Player named "Bob" so that it returns the key #? For example it should return 4.
Would array_search() work in this case? How would it be formatted?

Using array_filter will return you a new array with only the matching keys.
$playerName = 'bob';
$bobs = array_filter($players, function($player) use ($playerName) {
return $player->name === $playerName;
});

According to php docs, array_search would indeed work:
$players = array(
'Mike',
'Chris',
'Steve',
'Bob'
);
var_dump(array_search('Bob', $players)); // Outputs 3 (0-index array)
-- Edit --
Sorry, read post to quick, didn't see you had an array of objects, you could do something like:
$playersScalar = array(
'Mike',
'Chris',
'Steve',
'Bob'
);
class Player
{
public $name;
public $lvl;
}
foreach ($playersScalar as $playerScaler) {
$playerObject = new Player;
$playerObject->name = $playerScaler;
$playerObjects[] = $playerObject;
}
function getPlayerKey(array $players, $playerName)
{
foreach ($players as $key => $player) {
if ($player->name === $playerName) {
return $key;
}
}
}
var_dump(getPlayerKey($playerObjects, 'Steve'));

Related

Why my code generate at the and the arrays of keys: "0"

I created a cache from xml, and by a construct I generate the object which finally become the arrays. And everything would be ok, if the key of these arrays wasnt "0". I dont know how it works. I searched the information how to change the class, or how to replace the keys. I am stuck. Could you help me with this.
$xml = simplexml_load_file($cache);
}
class Property {
public $xmlClass;
public $elemClass = '';
public $result_array = [];
public $data = '';
public function __construct($xml,$elem) {
$this->xmlClass=$xml;
$this->elemClass=$elem;
foreach($xml->list->movie as $value) {
$data = $value->$elem;
$this->result_array[] = $data;
}
}
public function getResult() {
return $this->result_array;
}
}
$result_zn = new Property($xml,'zn');
$result_au = new Property($xml,'au');
$result_ti = new Property($xml, 'ti');
$zn = $result_zn->getResult();
$au = $result_au->getResult();
$ti = $result_ti->getResult();
I think you can use the function array_values() to get the key 0,like this:
$arr = array(
'1' => 'cat',
'2' => 'dog'
);
$newarr = array_values($arr);
print_r($newarr);
and the result is :
Array ( [0] => cat [1] => dog )

Generating a php object, two levels deep

I'm new to php - objects and arrays, especially. Coming from a JavaScript world, I'm having a modicum of trouble understanding the right way to construct objects, that may easily be iterated.
I'd like to create an object (or array - although I suspect an object would be more suitable) with the following structure:
$client_body:
$cst:
$title: 'Unique string'
$copy: function_result()
$ser:
$title: 'Unique string'
$copy: function_result()
$imp
$title: 'Unique string'
$copy: function_result()
...
I've been trying with variations on the following, but with numerous errors:
$client_body = new stdClass();
$client_body->cst->title = 'Client case study';
$client_body->cst->copy = get_field('client_cst');
$client_body->ser->title = 'Our service';
$client_body->ser->copy = get_field('client_ser');
...
And it seems that, using this approach, I'd have to use a new stdClass invocation with each new top-level addition, which seems a little verbose.
Could someone point me in the right direction?
You can just typecast an array to an object:
$client_body = (object)array(
"cst" => (object)array(
"title" => "Unique string",
"copy" => function_result()
)
);
You can try this object class more OOP:
<?php
class ClientBody{
protected $cst;
protected $ser;
protected $imp;
public function __construct($cst = '', $ser ='', $imp = '')
{
$this->cst = $cst;
$this->ser = $ser;
$this->imp = $imp;
}
public function getCst()
{
return $this->cst;
}
public function getSer()
{
return $this->ser;
}
public function getImp()
{
return $this->imp;
}
public function setCst($value)
{
$this->cst = $value;
}
public function setSer($value)
{
$this->ser = $value;
}
public function setImp($value)
{
$this->imp = $value;
}
}
$myObject = new ClientBody('toto', 'titi', 'tata');
echo $myObject->getCst(); // output 'toto'
echo $myObject->getSer(); // output 'titi'
echo $myObject->getImp(); // output 'tata'
Or you could use json_decode($client_body, TRUE);

Reference PHP array by multiple indexes

This may be some sort of weird longer shortcut, and please correct me if I'm mistaken in this train of thought...
I have a matrix of data that looks like:
unique_id | url | other random data...
unique_id | url | other random data...
unique_id | url | other random data...
I want to be able to reference an item by either it's url, or it's unique_id - is there a fancy way to do this?
I suppose the cheating solution would be to just make two arrays, but I was wondering if there is a better way.
Only way I can think of that doesn't involve iterating the array for each search (see Jacob's answer) is to store references to each item in two arrays.
Edit: As the URLs and IDs cannot collide, they may be stored in the same reference array (thanks Matthew)
$items; // array of item objects
// Use objects so they're implicitly passed by ref
$itemRef = array();
foreach ($items as $item) {
$itemRef[$item->unique_id] = $item;
$itemRef[$item->url] = $item;
}
// find by id
$byId = $itemRef[$id];
// find by url
$byUrl = $itemRef[$url];
You could probably encapsulate this nicely using a collection class that implements getById() and getByUrl(). Internally, it could store the references in as many arrays as is necessary.
Of course, what you're essentially doing here is creating indexed result sets, something best left to database management systems.
Try something like this:
function selectByIdOrURL($array, $data) {
foreach($array as $row) {
if($row['unique_id'] == $data || $row['url'] == $data) return $row;
}
return NULL;
}
$array = array(
array('unique_id' => 5, 'url' => 'http://blah.com'),
array('unique_id' => 3, 'url' => 'http://somewhere_else.com')
);
$found = selectByIdOrURL($array, 5); //array('unique_id' => 5, 'url' => 'http://blah.com')
$nfound = selectByIdOrURL($array, 10); //NULL
It appears your fancy solution was only available as of PHP 5.5.
You can combine the use of array_search and array_column to fetch your entry in a single line of code:
$items = [
[
'unique_id' => 42,
'url' => 'http://foo.com'
],
[
'unique_id' => 57,
'url' => 'http://bar.com'
],
[
'unique_id' => 36,
'url' => 'http://example.com'
],
];
$bar = $entries[array_search(57, array_column($items, 'unique_id'))];
var_dump($bar);
//outputs
array (size=2)
'unique_id' => int 57
'url' => string 'http://bar.com' (length=14)
Surely an object would be the easy way?
class Item {
public $unique_url;
public $url;
public $other_data;
public function __construct($unique_url, $url, $other_data)
{
$this->unique_url = $unique_url;
$this->url = $url;
$this->other_data = $other_data;
}
}
class ItemArray {
private $items = array();
public function __construct()
{
}
public function push(Item $item)
{
array_push($items, $item); //These may need to be reversed
}
public function getByURL($url)
{
foreach($items as $item)
{
if($item->url = $url)
{
return $item;
}
}
}
public function getByUniqueURL($url)
{
foreach($items as $item)
{
if($item->unique_url = $unique_url)
{
return $item;
}
}
}
}
Then use it with
$itemArray = new ItemArray();
$item = new Item("someURL", "someUniqueURL","some other crap");
$itemArray->push($item);
$retrievedItem = $itemArray->getItemByURL("someURL");
This technique has a little extra overhead due to object creation, but unless you're doing insane numbers of rows it would be fine.

How to format a simple PHP array of strings?

I have this simple function which I pass in an array of strings:
function myfunction( $arg = array() )
{
// do stuff to $arg...
// return a $string;
}
Simple so far, but I need some of the strings in the $arg array to be formatted, while some remain unformatted. I can't figure out how to do it?
Say I run this $arg through myfunction():
echo myfunction( array( 'format me!', 'do not format me!' ) );
My tiny little brain can't figure out how to tell myfunction() that the first value in $arg array needs to have formatting, and it should not format the second value.
I thought about an associative array, but I think that could be the wrong approach because of having identical indexes.
echo myfunction( array( 'format' => 'hi', 'format' => 'bye', 'noformat' => 'foo');
Just looking for a "nudge" in the right direction.
EDIT 1:
Forgot to mention, I can only have one $arg array because I need the keys to be in a specific order.
EDIT 2:
The $arg array can have as many keys as the user wants.
You can do:
function myfunction(array $format, array $noformat) {
...
}
or
function myfunction(array $strings) {
foreach ($strings['format'] as $format) {
// do stuff
}
foreach ($strings['noformat'] as $noformat) {
// do stuff
}
}
with:
myfunction(array(
'format' => array('one', 'two', 'three'),
'noformat' => array('four', 'five', 'six'),
));
If (and only if) the strings are unique you can put them in the key instead of the value:
$strings = array(
'one' => true,
'two' => false,
'three' => false,
'four' => true,
);
myfunction($strings);
with:
function myfunction(array $strings) {
foreach ($strings as $k => $v) {
if ($v) {
// format string
}
}
}
But since you can't have duplicate keys this method falls down if you have repeated strings.
Instead of having myfunction() take an array as an argument, why not just have it take a single element as the argument. Then you can use array_map to process each element of the array.
Sort of like this:
function myfunction($element) {
if( do-formatting) {
//do your formatting stuff
$element = formatted-stuff
}
return $element
}
$arr = array("format me", "don't format me");
//This calls myfunction on each element of the array
//A new array is returned where each element has been replaced
//by the return value from myfunction.
$arr = array_map('myfunction', $arr);
Here is how I would to it.
The implementation of __tostring is not mandatory but is syntax sugar inside the myFunction.
<?php
class MyString{
private $_value;
public $to_format;
public function __construct($str, $to_format = true){
$this->_value = $str;
$this->to_format = $to_format;
}
public function __tostring(){
return $this->_value;
}
}
$args = array( new MyString('format me'), new MyString('Not me!', false) );
This is oddly similar to how table rows are formatted (with a different formatting function for each row). I would provide the data as a single array, then provide formatting information as a second array based on keys. For instance:
function rowFormatter(Array $data, Array $format=array()) {
ob_start();
foreach ($data as $key => $value) {
if (isset($format[$key]))
echo call_user_func($format[$key],$value);
else
echo $value;
}
return ob_get_clean();
}
function makeBold($x) { return '<b>'.$x.'</b>'; }
rowFormatter(array( "Hello", "Mister" ), array( 0 => 'makeBold' ));
I don't know what these strings you are formatting actually represent, so I used generic naming on these classes:
class LineItem
{
var $value;
function __construct($val)
{
$this->value = $val;
}
function __toString()
{
return $this->value;
}
function getFormatted( $formatter )
{
return $this->value;
}
}
class FormattedLineItem extends LineItem
{
function getFormatted( $formatter )
{
return $formatter( $this->value );
}
}
function myfunction( $arr=array() )
{
$fnFormat = function($str) {
return ucwords($str);
};
foreach($arr as $obj)
{
$str = $obj->getFormatted($fnFormat);
echo "$str\n";
}
}
$arr = array(
new FormattedLineItem('format me!'),
new LineItem('do not format me!')
);
myfunction($arr);
The idea is to use separate classes to distinguish between strings.

Populating an object's properties with an array?

I want to take an array and use that array's values to populate an object's properties using the array's keynames. Like so:
$a=array('property1' => 1, 'property2' => 2);
$o=new Obj();
$o->populate($a);
class Obj
{
function Populate($array)
{
//??
}
}
After this, I now have:
$o->property1==1
$o->property2==2
How would I go about doing this?
foreach ($a as $key => $value) {
$o->$key = $value;
}
However, the syntax you are using to declare your array is not valid. You need to do something like this:
$a = array('property1' => 1, 'property2' => 2);
If you don't care about the class of the object, you could just do this (giving you an instance of stdClass):
$o = (Object) $a;
Hm. What about having something like
class Obj
{
var properties = array();
function Populate($array)
{
this->properties = $array;
}
}
Then you can say:
$o->properties['property1'] == 1
...

Categories