for vs foreach - reference to array elements - php

I had an array which contained lines of a file that I had to process, each element of the array was a line of the file. After processing I implode the file, write it out and use it.
When I tried to use foreach for this, it didn't work. I had a suspicion that it was creating a copy of the element rather than referencing the element directly, so used a for loop instead, which did work.
My question is, is there some way to use a foreach loop in this scenario or when redefining elements of an array must you always use a for loop?
Example code:
$fileArray = file('blah.txt');
foreach ($fileArray as $thisLine) {
if ( condition ) {
$thisLine = "changed state";
}
}
$newFileArray = implode('',$fileArray);
Didn't work vs:
$fileArray = file('blah.txt');
for ($x=0;$x<count($fileArray);$x++) {
if ( condition ) {
$fileArray[$x] = "changed state";
}
}
$newFileArray = implode('',$fileArray);
Which worked fine.

$fileArray = file('blah.txt');
foreach ($fileArray as $key => $thisLine) {
if ( condition ) {
$fileArray[$key] = "changed state";
}
}
$newFileArray = implode('',$fileArray);
or passing by reference directly:
$fileArray = file('blah.txt');
foreach ($fileArray as &$thisLine) {
if ( condition ) {
$thisLine = "changed state";
}
}
$newFileArray = implode('',$fileArray);

In your first example, you're altering a copy of a string. $thisLine is not a reference to the element in the array, but a copy of the array element.
In your second example, you are altering the array directly. You could use a foreach, but only if you go back to referencing the array:
foreach ($fileArray as $key => $thisLine) {
if ( condition ) {
$fileArray[$key] = "changed state";
}
}

Related

PHP parse nexted XML with unique attributes

I have a nested XML that I need to traverse and get not only the nodes, but also the attribute key and value which are each different.
I tried writing a recursive function in PHP to get what I was looking for. My XML looks like the following...
<document>
<character>
<literal>name</literal>
<codepoint>
<cp_value cp_type="ucs">4e9c</cp_value>
<cp_value cp_type="jis208">16-01</cp_value>
</codepoint>
<radical>
<rad_value rad_type="classical">7</rad_value>
<rad_value rad_type="nelson_c">1</rad_value>
</radical>
<meaning_group>
<meaning>this</meaning>
<meaning>that</meaning>
</meaning_group>
</character>
...
</document>
The problem is that not all [character] nodes have the exact same children.
I am trying to pull the attribute key and value to combine into one key, then associate the value as the value. If there is no attribute, I want to use the tag name as the key. Also, some children have the same name with no attribute. In this case, I want to just put them in one field separated by a line break. Thanks!!
["literal"] => "name",
["cp_type-ucs"] => "4e9c",
["cp_type-jis208"] => "16-01",
["rad_type-classical"] => "7",
["rad_type-nelson_c"] => "1",
["meaning"] => "this\nthat"
That's the array I want to output...
Any and all help would be greatly appreciated! Thanks!
EDIT: Added some code that I can use to traverse through the layers and get the tag names to echo, but for some reason, they won't populate the array. Just the "character" tag will go in the array.
function ripXML($file) {
$xml = simplexml_load_file ( $file );
return (peelTags ( $xml , array()) );
}
function peelTags($node, $tag) {
// find if there are children. (IF SO, there shouldn't be
$numChildren = #count ( $node->children () );
if ($numChildren != 0) {
foreach ( $node->children () as $child ) {
$tag [] = $child->getName ();
peelTags ( $child, $tag);
echo "<br />Name = " . $child->getName ();
}
}
return $tag;
}
$file = "dictionarytest.xml";
print_r ( ripXML ( $file ) );
EDIT 2 -
I figured it out finally. It might be a bit messy and not the best way to go about it, but it solved the problem that I was faced with. In case anyone else needed something similar, here it is!
$_SESSION ["a"] = array ();
$_SESSION ["c"] = 0;
function ripXML($file) {
$xml = simplexml_load_file ( $file );
return (peelTags ( $xml, array () ));
}
function peelTags($node, $tag) {
// find if there are children. (IF SO, there shouldn't be
$numChildren = #count ( $node->children () );
if ($numChildren != 0) {
foreach ( $node->children () as $child ) {
peelTags ( $child, $tag );
$tag = $child->getName ();
if ($tag == "literal") {
$_SESSION ["c"] ++;
}
$value = trim($child->__toString ());
if (isset ( $value ) && $value != "") {
if ($child->attributes ()) {
foreach ( $child->attributes () as $k => $v ) {
if (isset ( $v ) && $v != "") {
$_SESSION ["a"] [$_SESSION ["c"]] [$k . "_" . $v] = $value;
}
}
} else {
$_SESSION ["a"] [$_SESSION ["c"]] [$tag] = $value;
}
}
}
}
return 1;
}
$file = "dictionarytest.xml";
print_r ( ripXML ( $file ) );
print_r ( $_SESSION ["a"] );
I used global session variables to store the array and counter for the recursive algorithm. I don't know if anyone has a better suggestion. I would like to optimize this function if possible. I was testing it on an XML file of only 5 entries, but my real file will have over 4000.
... confusing. i did not syntax check or test this, but i think its something like this..
$domd=new DOMDocument();
$domd->loadXML($xml);
$interestingdomnode=$domd->getElementsByTagName("character")->item(0);
$parsed_info=array();
$parsed_info['literal']=$interestingdomnode->getElementsByTagName("literal")->item(0)->textContent;
foreach($interestingdomnode->getElementsByTagName("cp_value") as $cp){
$parsed_info["cp_type-".$cp->cp_type]=$cp->textContent
}
foreach($interestingdomnode->getElementsByTagName("rad_type") as $cp){
$parsed_info["rad_type-".$cp->rad_type]=$cp->textContent
}
$parsed_info['meaning']='';
foreach($interestingdomnode->getElementsByTagName("meaning") as $cp){
$parsed_info['meaning'].=$cp->textContent.PHP_EOL;
}
var_dump($parsed_info);

Getting certain array from a multidimensional array

I have multidimensional array which is a query returning the info from a table named 'users'. In another part of my code I need to get the records of only one certain user and I want to take it using the array I mentioned above. It's of type:
This is probably what you're looking for:
$row = NULL;
foreach ($parent as $key => $child)
{
if (1 == $child['id'])
{
$row = $child; break;
}
}
if (isset($row))
{
// Stuff to do with the chosen row
}
$main = // your array
foreach ($main as $index => $sub) {
foreach ($sub as $subIndex => $item) {
if ($item['id'] == xxx) {
return $main[$index][$subIndex];
}
}
}
IMO using for loops (rather that foreach) would make it easier for you to reference the variable you need (by using the for loop variable to look up the appropriate row in the array)
HTH,
David

Anything to attend when changing values in a multidimensional foreach() loop?

I got a site that executes the following code
$keywords = ($_SESSION[$_POST['ts']]);
print_r($keywords);
foreach ($keywords as $keyword) {
foreach ($keyword['whitelist'] as $entry) {
foreach ($_POST as $key => $value) {
if ($key == $entry['encoded_url']) {
$entry['ignore'] = $value;
$decodedURL = $this->base64->url_decode($entry['encoded_url']);
if ($value == 'noignore') {
echo "found!<br />";
$this->blacklist_model->remove($decodedURL);
$html = $this->analyse->getHTML($decodedURL);
$entry['html'] = $html[0];
$entry['loading_time'] = $html[1];
}
if($value == 'alwaysignore') {
$this->blacklist_model->add($decodedURL);
}
}
}
}
}
print_r($keywords);
The output looks like this:
http://pastebin.com/B3PtrqjB
So, as you see, there are several "found!"s in the output, so the if clause actually gets executed a few times and I expected the second array to contain new data like 'html', but, as you see, nothing changes. Is there anything to attend when changing values in multidimensional foreach() loops?
foreach creates a copy of the array and loops through that. Modifying values doesn't work.
You can get around this, though, by using references.
foreach ($keywords as &$keyword) {
foreach ($keyword['whitelist'] as &$entry) {
foreach ($_POST as $key => &$value) {
...
}
}
}
With that you can modify $value and it WILL affect the original array.
You suppose to change the Original array.
$entry is just an instance / unconnected node.
you need to change $keywords, and not its on the fly created nodes.

php array keys problem

I have the following code:
$rt1 = array
(
'some_value1' => 'xyz1',
'some_value2' => 'xyz2',
'value_1#30'=>array('0'=>1),
'value_2#30'=>array('0'=>2),
'value_3#30'=>array('0'=>3),
'value_1#31'=>array('0'=>4),
'value_2#31'=>array('0'=>5),
'value_3#31'=>array('0'=>6),
'some_value3' => 'xyz3',
'some_value4' => 'xyz4',
);
$array_30 = array
(
'0'=>1,
1=>'2',
2=>'3'
);
$array_31 = array
(
'0'=>4,
'1'=>'5',
'2'=>'6'
);
I need to make it an array and insert the array_30 and array_31 into a DB.
foreach($rt1 as $value){
$rt2[] = $value['0'];
}
The question was updated, so here is an updated answer. Quick check, you should really try and update this to whatever more generic purpose you have, but as a proof of concept, a runnable example:
<?php
$rt1 = array
(
'some_value1' => 'xyz1',
'some_value2' => 'xyz2',
'value_1#30'=>array('0'=>1),
'value_2#30'=>array('0'=>2),
'value_3#30'=>array('0'=>3),
'value_1#31'=>array('0'=>4),
'value_2#31'=>array('0'=>5),
'value_3#31'=>array('0'=>6),
'some_value3' => 'xyz3',
'some_value4' => 'xyz4',
);
$finalArrays = array();
foreach($rt1 as $key=>$value){
if(is_array($value)){
$array_name = "array_".substr($key,-2);
${$array_name}[] = $value['0'];
}
}
var_dump($array_30);
var_dump($array_31);
?>
will output the two arrays with the numbers 1,2,3 and 4,5,6 respectivily
i assume you want to join the values of each of the second-level arrays, in which case:
$result = array();
foreach ($rt1 as $arr) {
foreach ($arr as $item) {
$result[] = $item;
}
}
Inspired by Nanne (which reminded me of dynamically naming variables), this solution will work with every identifier after the \#, regardless of its length:
foreach ( $rt1 as $key => $value )
{
if ( false == strpos($key, '#') ) // skip keys without #
{
continue;
}
// the part after the # is our identity
list(,$identity) = explode('#', $key);
${'array_'.$identity}[] = $rt1[$key]['0'];
}
Presuming that this is your actual code, probably you will need to copy the array somewhere using foreach and afterwards create the new array as you wish:
foreach($arr as $key => $value) {
$arr[$key] = 1;
}

Selecting custom indexes in array

I have an nested array that's a mix of words and numbers. It looks like this conceptually. I need to process only numbered indexes such as 15 and 28. I guess this means that I can't use a foreach loop (or is there a way I could). How would you do this?
myarray = (
someindex = (
field1 =
field2 =
);
15 = (
field1 =
field2 =
);
28 = (
field1 =
field2 =
);
anothertext = (
field1 =
field2 =
);
);
foreach($myarr as $key => $item)
{
if(is_int($key))
{
// Do processing here
}
}
Yes, that will loop through every item in the array, so if you wanted to process the other items separately, you could just add in an else block.
Edit: Changed is_numeric to is_int. See comments for explanation.
You can use foreach
foreach($myarray as $key=>$value)
{
if(is_int($key))
{
//process the entry as you want
}
}
You could use a FilterIterator with foreach:
class IntKeyFilterIterator extends FilterIterator {
public function accept() {
return is_int(parent::key());
}
}
$it = new IntKeyFilterIterator(new ArrayIterator($array));
foreach ($it as $value) {
// Will only have those with int keys
}
Another version. Grep the source array for any purely numeric keys, then loop over that result array and do the processing.
$keys = preg_grep('/^\d+$/', array_keys($myarray)) {
foreach($keys as $key) {
doSomething($myarray[$key]);
}

Categories