I have a working script, but I'm sure that my method of managing arrays could be better. I've searched for a solution and haven't found one, but I'm sure that I should be using the functionality of associative arrays to do things more efficiently.
I have two arrays, one from a CSV file and one from a DB. I've created the CSV array as numeric and the DB array as associative (although I'm aware that the difference is blurry in PHP).
I'm trying to find a record in the DB array where the value in one field matches a value in the CSV array. Both arrays are multi-dimensional.
Within each record in each array there is a reference number. It appears once in the CSV array and may appear in the DB array. If it does, I need to take action.
I'm currently doing this (simplified):
$CSVarray:
('reference01', 'blue', 'small' ),
('reference02', 'red', 'large' ),
('reference03', 'pink', 'medium' )
$Dbarray:
(0 => array(ref=>'reference01',name=>"tom",type=>"mouse"),
(1 => array(ref=>'reference02',name=>"jerry",type=>"cat"),
(2 => array(ref=>'reference03',name=>"butch",type=>"dog"),
foreach ($CSVarray as $CSVrecord) {
foreach ($Dbarray as $DBrecord) {
if ($CSVarray[$numerickey] == $DBrecord['key'] {
do something with the various values in the $DBrecord
}
}
}
This is horrible, as the arrays are each thousands of lines.
I don't just want to know if matching values exist, I want to retrieve data from the matching record, so functions like 'array_search ' don't do what I want and array_walk doesn't seem any better than my current approach.
What I really need is something like this (gibberish code):
foreach ($CSVarray as $CSVrecord) {
WHERE $Dbarray['key']['key'] == $CSVrecord[$numerickey] {
do something with the other values in $Dbarray['key']
}
}
I'm looking for a way to match the values using the keys (either numeric or associative) rather than walking the arrays. Can anyone offer any help please?
use a hash map - take one array and map each key of the record it belongs to, to that record. Then take the second array and simply iterate over it, checking for each record key if the hashmap has anything set for it.
Regarding your example:
foreach ($DBarray as $DBrecord){
$Hash[$record[$key]] = $DBrecord;
}
foreach ($CSVarray as $record){
if (isset($Hash[$record[$CSVkey]])){
$DBrecord = $Hash[$record[$CSVkey]];
//do stuff with $DBrecord and $CSVrecord
}
}
this solution works at O(n) while yours at O(n^2)...
You can use foreach loops like this too:
foreach ($record as $key => $value) {
switch($key)
{
case 'asd':
// do something
break;
default:
// Default
break;
}
}
A switch may be what you are looking for also :)
Load CSV into the db, and use db (not db array) if possible for retrieval. Index the referenceid field.
Related
I feel like i'm missing something quite simple. What are some of the best ways to iterate through combinations of $key names -- doing something different for each-- in a php foreach loop?
I have a number of values in an array with key values that follow the same naming format.
Example:
$rec_items['title3'] = implode($meta['title3']);
$rec_items['title4'] = implode($meta['title4']);
$rec_items['title5'] = implode($meta['title5']);
The $rec_items array also contains other values that do not follow this naming convention (or data type).
I'm looping through $rec_items with a foreach loop. I would like to be able to dynamically cycle through key names in $rec_items, and 'do something' when a key is found that matches title*. I've tried pushing numeric numbers from a counter variable into key names to be searched for (like below):
foreach ($rec_items as $key => $value){
$c = 0;
if(!empty($key[${'title'.$c}]){
$c++;
//do something
}
I believe that I cannot pass the value ${'title'.$c} into $key[] and have tried to pass the value of ${'title'.$c} as a string with no luck.
I just share the above to try to highlight what i'm trying to achieve.
(1) dynamically loop through key names in the format 'title*'
(2) if the key name is present in the $rec_items array ... do something.
I'm not sure what your original code was trying for; you appeared to be treating the array key like an array itself? Using a variable variable? Anyway, you just need a simple string search of the key. You can use regular expressions or whatever you like for more complex matching.
<?php
$rec_items = ["foo"=>12, "bar"=>34, "title1"=>56, "title2"=>78, "baz"=>90];
foreach ($rec_items as $k=>$v) {
if (strpos($k, "title") === 0) {
echo "$k = $v\n";
}
}
Output:
title1 = 56
title2 = 78
Bottom line, I have a huge multidimensional array returned by ldap_get_entries that I am trying to parse into different groups based on the location attribute.
In PowerShell I could do something like:
$location1 = Get-ADUser -Filter * | ? {?_.l -eq "test_location"}
What I am currently doing within PHP (as far as my brain will let me go) is something like:
Foreach ($records as $record) {
if (isset($record['l'])) {
switch ($record['l'][0]) {
case "test_location1":
$location1[] = $record;
continue 2;
case "test_location2":
$location2[] = $record;
continue 2;
}
}
}
I then use a foreach loop (alternative syntax) against an array of location variables ($location1, $location2), that I used the above method, to sort records into their appropriate location variable. In this way, I build an HTML table grouping each locations records together. I build a new table for each location with some HTML code in between each group, so I don't believe sorting the ldap results by location will work, as it would output one large table.
Is there any way to specify a WHERE clause in PHP? So that I can assign all records in an array with a matching key value to variable1 and all records in an array with a different matching key value to variable2.
I am assuming I am tackling this in an amateur scripting way.. If there is an easier way to accomplish this task (maybe skipping the assign records to variables part), or any sort of "best practice" I am missing here, I am up for learning it.
Thanks in advance!!
As far as I understood your question you want something like this:
$recordsSorted = array();
foreach ($records as $record) {
if (! isset($record['l'])) {
continue;
}
$recordsSorted[$records['l'][0]][] = $record;
}
ksort($recordsSorted);
foreach($recordsSorted as $location => $records) {
usort($records, function($a, $b){
return strnatcasecmp($a['uid'][0], $b['uid'][0]);
});
echo '<h1>$location</h1><ul>';
foreach ($records as $record) {
echo '<li>' . $record['dn'] . '</li>';
}
echo '</ul>';
}
This will first put the first entry to the location-attribute of an entry as key of an array. That key can then be used to sort the array.
To output the content then it iterates over the new array and sorts the content - in this case using the first value of the uid-attribute (change the uid to whatever attribute you need). This sorted content is then output to HTML.
the first array $recordsSorted might look somewhat like this:
array(
'London' => array(
array(<entry from the LDAP>),
array(<another entry from LDAP>),
),
'Paris' => array(
array(<a further entry from the LDAP>),
),
'Berlin' => array(
array(<and even another entry from the LDAP>),
),
);
The result would then look somewhat like this:
<h1>Berlin</h1>
<ul>
<li>[UID of and even another entry from the LDAP]</li>
</ul>
<h1>London</h1>
<ul>
<li>[UID of another entry from LDAP]</li>
<li>[UID of entry from the LDAP]</li>
</ul>
<h1>Paris</h1>
<ul>
<li>[UID of a further entry from the LDAP]</li>
</ul>
Does that look like it could help you?
Is there any way to maintain a sorted array of objects?
For example, if I have an object with properties ID, Date, Name and a collection of these objects:
$col = array();
public function addNewObject($id, $date, $name)
{
$col[] = new Object($id, $date, $name);
//but instead of appending, it should place it by Name desc
}
If I call something like getObjects, it would return the items in the collection by Name desc.
I think there were some answers for getting objects back in a sorted order, but for efficiency, I would think it be better to sort at insert as the "sort by" variable in my case will never change.
UPDATE:
So based on the comments, I should resort the whole array each time something is added but that seems a bit memory intensive...
Since the array would always be in sorted order to start out with I can identify the location where I want to insert by traversing the array (would this be efficient, is there a better way?). Once I find that how could I "insert" a new object into the array?
I do not imagine that the array will be very large but I would like to implement this the most efficient way possible.
If you're not keen on resorting the array after you add (although I'd recommend it; realistically this wont be a performance issue and it keeps the code readable.
However, if you definitely don't want to do this then you can, as you said, traverse the array and find out where to insert:
$col = array();
public function addNewObject($id, $date, $name){
//Find the index to insert at
$index = 0;
foreach($col as $i => $item){
if($item->name > $name){
//This item is after the item we want to insert.
//Use the previous index and stop traversing
break;
}
$index = $i;
}
$col = array_splice($col, $index, 0, new Object($id, $date, $name));
}
Using array_splice to insert at an arbritary position thanks to https://stackoverflow.com/a/3797526/505722
this is a good example of a function that sorts an array from whatever key you want it sorted by
http://www.php.net/manual/en/function.sort.php#99419
in your example you should run it like :
array_sort($col, 'Name', SORT_DESC));
take in mind that every time you add a new item to the array the whole array is sorted each time
I have 2 sets of arrays:
$dates1 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data1 = array('5','3','7','7','22','18');
// for this dataset, the value on 9/12 is 5
$dates2 = array('9/14','9/15');
$data2 = array('12','1');
As you can see the 2nd dataset has fewer dates, so I need to "autofill" the reset of the array to match the largest dataset.
$dates2 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data2 = array('','','12','1','','');
There will be more than 2 datasets, so I would have to find the largest dataset, and run a function for each smaller dataset to properly format it.
The function I'd create is the problem for me. Not even sure where to start at this point. Also, I can format the date and data arrays differently (multidimensional arrays?) if for some reason that is better.
You can do this in a pretty straightforward manner using some array functions. Try something like this:
//make an empty array matching your maximum-sized data set
$empty = array_fill_keys($dates1,'');
//for each array you wish to pad, do this:
//make key/value array
$new = array_combine($dates2,$data2);
//merge, overwriting empty keys with data values
$new = array_merge($empty,$new);
//if you want just the data values again
$data2 = array_values($new);
print_r($data2);
It would be pretty easy to turn that into a function or put it into a for loop to operate on your array sets. Turning them into associative arrays of key/value pairs would make them easier to work with too I would think.
If datas are related will be painful to scatter them on several array.
The best solution would be model an object with obvious property names
and use it with related accessor.
From your question I haven't a lot of hint of what data are and then I have to guess a bit:
I pretend you need to keep a daily log on access on a website with downloads. Instead of using dates/data1/data2 array I would model a data structure similar to this:
$log = array(
array('date'=>'2011-09-12','accessCount'=>7,'downloadCount'=>3),
array('date'=>'2011-09-13','accessCount'=>9), /* better downloadsCount=>0 though */
array('date'=>'2011-09-15','accessCount'=>7,'downloadCount'=>3)
...
)
Using this data structure I would model a dayCollection class with methods add,remove,get,set, search with all methods returning a day instance (yes, the remove too) and according signature. The day Class would have the standard getter/setter for every property (you can resolve to magic methods).
Depending on the amount of data you have to manipulate you can opt to maintain into the collection just the object data (serialize on store/unserialize on retrieve) or the whole object.
It is difficult to show you some code as the question is lacking of details on your data model.
If you still want to pad your array than this code would be a good start:
$temp = array($dates, $data1, $data2);
$max = max(array_map('count',$temp));
$result = array_map( function($x) use($max) {
return array_pad($x,$max,0);
}, $temp);
in $result you have your padded arrays. if you want to substitute your arrays do a simple
list($dates, $data1, $data2) = array_map(....
You should use hashmaps instead of arrays to associate each date to a data.
Then, find the largest one, cycle through its keys with a foreach, and test the existence of the same key in the small one.
If it doesn't exist, create it with an empty value.
EDIT with code (for completeness, other answers seem definitely better):
$dates_data1 = array('9/12'=>'5', '9/13'=>'3', '9/14'=>'7' /* continued */);
$dates_data2 = array('9/14'=>'12', '9/15'=>'1');
#cycle through each key (date) of the longest array
foreach($dates_data1 as $key => $value){
#check if the key exists in the smallest and add '' value if it does not
if(!isset( $date_data2[$key] )){ $date_data2[$key]=''; }
}
i have two arrays i.e$ar1=array("Mobile","shop","software","hardware");and$arr2=arry("shop","Mobile","shop","software","shop")
i want to compare the elements of arr2 to arr1 i.e
foreach($arr2 as $val)
{
if(in_array($val, $arr1))
{
//mycode to insert data into mysql table
variable++; // here there should be a variable that must be increamented when ever match found in $arr2 so that it can be saved into another table.
}
$query="update table set shop='$variable',Mobile='$variable'.......";
}
the $variable should be an integer value so that for later use i can use this variable(shop i.e in this example its value should be 3) to find the match.
My question is how can i get the variable that will increamented each time match found.
Sorry, I don't fully understand the purpose of your code. You can use array_intersect to get common values and array_diff to get the unique values when comparing two arrays.
i want to compare the elements of arr2 to arr1 i.e
Then you are essentially doing the same search for shop three times. It's inefficient. Why not sort and eliminate duplicates first?
Other issues. You are comparing arr2 values with the ones in arr1, which means the number of repetation for "shop" will not be 3 it will be one. Doing the opposite might give you the number of repetation of arr1[1] in arr2 =3.
There are multitude of ways to solve this problem. If efficiency is required,you might wish to sort so you don't have to go beyond a certain point (say s). You can learn to use indexes. Infact the whole datastructure is revolved around these kinds of things - from quick and dirty to efficient.
Not sure I understand the connection between your two arrays. But you can use this to count how many items are in your second array:
$items = array("shop","Mobile","shop","software","shop");
$count = array();
foreach($items as $item)
{
if(isset($count[$item]))
{
$count[$item]++;
}
else
{
$count[$item] = 1;
}
}
print_r($count); // Array ( [shop] => 3 [Mobile] => 1 [software] => 1 )