I want to check only the value [id] for duplicates, and remove all keys where this "field" [id] is a duplicate.
Example: If I have numbers 1,2,1. I want the result to be 2, not 1,2. And criteria for duplicates is determined only by checking [id], not any other "field".
Original array:
Array
(
[0] => Array
(
[name] => John
[id] => 123
[color] => red
)
[1] => Array
(
[name] => Paul
[id] => 958
[color] => red
)
[2] => Array
(
[name] => Jennifer
[id] => 123
[color] => yellow
)
)
The result I want:
Array
(
[0] => Array
(
[name] => Paul
[id] => 958
[color] => red
)
)
I agree with everyone above, you should give us more information about what you've tried, but I like to code golf, so here's a completely unreadble solution:
$new_array = array_filter($array, function($item) use (&$array){
return count(array_filter($array, function($node) use (&$item){
return $node['id'] == $item['id'];
})) < 2;
});
This should be fairly easy to accomplish with a couple of simple loops:
set_time_limit(0); // Disable time limit to allow enough time to process a large dataset
// $items contains your data
$id_counts = array();
foreach ($items as $item) {
if (array_key_exists($item['id'], $id_counts)) {
$id_counts[$item['id']]++;
} else {
$id_counts[$item['id']] = 1;
}
}
for ($i = count($items); $i >= 0; $i--) {
if ($id_counts[$items[$i]['id']] > 1) {
array_splice($items, $i, 1);
}
}
Result:
Array
(
[0] => Array
(
[name] => Paul
[id] => 958
[color] => red
)
)
While there are neater ways to do it, one advantage of this method is you're only creating new arrays for the list of ids and duplicate ids and the array_splice is removing the duplicates from the original array, so memory usage is kept to a minimum.
Edit: Fixed a bug that meant it sometimes left one behind
This is a very basic approach to the answer and I am sure there are much better answers however I would probably start by doing it the way I would on paper.
I look at the first index, check its value. Then I go through every other index making note of their index if the value is the same as my originally noted value. Once I have gone through the list if I have more than one index with that particular value I remove them all (starting with the highest index, so as to not affect indexes of the others while deleting).
Do this for all other indexes till you reach the end of the list.
It is long winded but will make sure it removes all values which have duplicates. and leaves only those which originally had no duplicates.
function PickUniques(array $items){
// Quick way out
if(empty($items)) return array();
$counters = array();
// Count occurences
foreach($items as $item){
$item['id'] = intval($item['id']);
if(!isset($counters[$item['id']])){
$counters[$item['id']] = 0;
}
$counters[$item['id']]++;
}
// Pop multiples occurence ones
foreach($counters as $id => $occurences){
if($occurences > 1){
unset($counters[$id]);
}
}
// Keep only those that occur once (in $counters)
$valids = array();
foreach($items as $item){
if(!isset($items[$item['id']])) continue;
$valids[$item['id']] = $item;
}
return $valids;
}
Try this one :)
Related
I will try to explain the data I'm working with first, then I'll explain what I hope to do with the data, then I'll explain what I've tried so far. Hopefully someone can point me in the right direction.
What I'm working with:
I have an array containing survey responses. The first two items are the two answers for the first question and responses contains the number of people who selected those answers. The last three items are the three answers for the other question we asked.
Array
(
[0] => Array
(
[survey_id] => 123456789
[question_text] => Have you made any changes in how you use our product this year?
[d_answer_text] => No
[responses] => 92
)
[1] => Array
(
[survey_id] => 123456789
[question_text] => Have you made any changes in how you use our product this year?
[answer_text] => Yes
[responses] => 30
)
[2] => Array
(
[survey_id] => 123456789
[question_text] => How would you describe your interaction with our staff compared to prior years?
[answer_text] => Less Positive
[responses] => 14
)
[3] => Array
(
[survey_id] => 123456789
[question_text] => How would you describe your interaction with our staff compared to prior years?
[answer_text] => More Positive
[responses] => 35
)
[4] => Array
(
[survey_id] => 123456789
[question_text] => How would you describe your interaction with our staff compared to prior years?
[answer_text] => No Change
[responses] => 72
)
)
What I want to achieve:
I want to create an array where the question_text is used as the key (or I might grab the question_id and use it instead), use the answer_text as a key, with the responses as the value. It would look something like this:
Array
(
[Have you made any changes in how you use our product this year?] => Array
(
[No] => 92
[Yes] => 30
)
[How would you describe your interaction with our staff compared to prior years?] => Array
(
[Less Positive] => 14
[More Positive] => 35
[No Change] => 72
)
)
Here's what I've tried:
$response_array = array();
foreach($result_array as $value){
//$responses_array['Our question'] = array('answer 1'=>responses,'answer 2'=>responses);
$responses_array[$value['question_text']] = array($value['answer_text']=>$value['responses']);
}
This does not work because each loop will overwrite the value for $responses_array[$question]. This makes sense to me and I understand why it won't work.
My next thought was to try using array_merge().
$responses_array = array();
foreach($result as $value){
$question_text = $value['question_text'];
$answer_text = $value['answer_text'];
$responses = $value['responses'];
$responses_array[$question_text] = array_merge(array($responses_array[$question_text],$answer_text=>$responses));
}
I guess my logic was wrong because it looks like the array is nesting too much.
Array
(
[Have you made any changes in how you use our product this year?] => Array
(
[0] => Array
(
[0] =>
[No] => 92
)
[Yes] => 30
)
My problem with array_merge is that I don't have access to all answers for the question in each iteration of the foreach loop.
I want to design this in a way that allows it to scale up if we introduce more questions with different numbers of answers. How can this be solved?
Sounds like a reduce job
$response_array = array_reduce($result_array, function($carry, $item) {
$carry[$item['question_text']][$item['answer_text']] = $item['responses'];
return $carry;
}, []);
Demo ~ https://eval.in/687264
Update
Remove condition (see #Phil comment)
I think you are looking for something like that :
$output = [];
for($i = 0; $i < count($array); $i++) {
$output[$array[$i]['question_text']] [$array[$i]['answer_text']]= $array[$i]['responses'];
}
print_r($output);
Slightly different approach than the answer posted, more in tune with what you'v already tried. Try This:
$responses_array = array();
$sub_array = array();
$index = $result[0]['question_text'];
foreach($result as $value){
$question_text = $value['question_text'];
$answer_text = $value['answer_text'];
$responses = $value['responses'];
if (strcmp($index, $question_text) == 0) {
$sub_array[$answer_text] = $responses;
} else {
$index = $question_text;
$responses_array[$index] = $sub_array;
$sub_array = array();
}
}
Edit: Found my mistake, updated my answer slightly, hopefully this will work.
Edit 2: Working with example here: https://eval.in/687275
I have two arrays (in PHP):
ArrayA
(
[0] => 9
[1] => 1
[2] => 2
[3] => 7
)
ArrayB
(
[0] => 1
[1] => 1
[3] => 8
)
I want to make two new arrays, where I have only the elements declared in both of the arrays, like the following:
ArrayA
(
[0] => 9
[1] => 1
[3] => 7
)
ArrayB
(
[0] => 1
[1] => 1
[3] => 8
)
In this example ArrayA[2] doesn't exist, so ArrayB[2] has been unset.
I wrote this for loop:
for ($i = 0, $i = 99999, $i++){
if (isset($ArrayA[$i]) AND isset($ArrayB[$i]) == FALSE)
{
unset($ArrayA[$i],$ArrayB[$i]);
}
}
But it's not great because it tries every index between 0 and a very big number (99999 in this case). How can I improve my code?
The function you're looking for is array_intersect_key:
array_intersect_key() returns an array containing all the entries of array1 which have keys that are present in all the arguments.
Since you want both arrays, you'll have to run it twice, with the parameters in opposite orders, as it only keeps keys from the first array. An example:
$arrayA_filtered = array_intersect_key($arrayA, $arrayB);
$arrayB_filtered = array_intersect_key($arrayB, $arrayA);
Also, although a for loop wasn't ideal in this case, in other cases where you find yourself needing to loop through sparse array (one where not every number is set), you can use a foreach loop:
foreach($array as $key => $value) {
//Do stuff
}
One very important thing to note about PHP arrays is that they are associative. You can't simply use a for loop, as the indices are not necessarily a range of integers. Consider what would happen if you applied this algorithm twice! You'd get out of bounds errors as $arrayA[2] and $arrayB[2] no longer exist!
I would iterate through the arrays using nested foreach statements. I.e.
$outputA = array();
$outputB = array();
foreach ($arrayA as $keyA => $itemA) {
foreach ($arrayB as $keyB => $itemB) {
if ($keyA == $keyB) {
$outputA[$keyA] = $itemA;
$outputB[$keyB] = $itemB;
}
}
This should give you two arrays, $outputA and $outputB, which look just like $arrayA and $arrayB, except they only include key=>value pairs if the key was present in both original arrays.
foreach($arrayA as $k=>$a)
if (!isset($arrayB[$k]))
unset($arrayA[$k];
Take a look to php : array_diff
http://docs.php.net/manual/fr/function.array-diff.php
I have a large array of scraped names and prices similar to the following:
Array([0] => apple3 [1] => £0.40 [2] => banana6 [3] => £1.80 [4] => lemon [5] => grape [6] => pear5 [7] => melon4 [8] => £2.32 [9] => kiwi [10] => £0.50)
I would like to remove the fruit names that are not immediately followed by a price. In the above example this would remove: [4] => lemon [5] => grape [6] => pear5 resulting in the following output:
Array([0] => apple3 [1] => £0.40 [2] => banana6 [3] => £1.80 [7] => melon4 [8] => £2.32 [9] => kiwi [10] => £0.50)
If the array needs to be converted to a string in order for me to do this that is not a problem, nor is adding values between the array items in order to aid with regex searches. I have so far been unable to find the correct regular expression to do this using preg_match and preg_replace.
The most important factor is the need to maintain the sequential order of the fruits and prices in order for me at a later stage to convert this into an associative array of fruits and prices.
Thanks in advance.
Why involve regular expressions? This is doable with a simple foreach loop wherein you iterate over the array and remove names that follow names:
$lastWasPrice = true; // was the last item a price?
foreach ($array as $k => $v) {
if (ctype_alpha($v)) {
// it's a name
if (!$lastWasPrice) {
unset($array[$k]); // name follows name; remove the second
}
$lastWasPrice = false;
}
else {
// it's a price
$lastWasPrice = true;
}
}
The following code does both of your tasks at once: getting rid of the fruit without value and turning the result into an associative array of fruits with prices.
$arr = array('apple', '£0.40', 'banana', '£1.80', 'lemon', 'grape', 'pear', 'melon', '£2.32', 'kiwi', '£0.50' );
preg_match_all( '/#?([^£][^#]+)#(£\d+\.\d{2})#?/', implode( '#', $arr ), $pairs );
$final = array_combine( $pairs[1], $pairs[2] );
print_r( $final );
First, the array is converted to a string, separated by '#'. The regex captures all groups of fruits with prices - each stored as a separate subgroup in the result. Combining them into an associative array is a single function call.
Something like this might help you
$array = ...;
$index = 0;
while (isset($array[$index + 1])) {
if (!is_fruit($array[$index + 1])) {
// Not followed by a fruit, continue to next pair
$index += 2;
} else {
unset($array[$index]); // Will maintain indices in array
$index += 1;
}
}
Not tested though. Also, you need to create the function is_fruit yourself ;)
Without reformatting it, I don't think you can do it with preg_match or preg_replace-- maybe, but nothing is coming to mind.
What is creating that array? If possible, I would alter it to look more like:
Array([apple] => £0.40 [banana] => £1.80 [lemon] => [grape] => '' [pear ] => '' [melon => £2.32 [kiwi] => £0.50)
Then array_filter($array) is all you'd need to clean it up. If you can't alter the way the original array is created I'd lean towards creating key/value array out of the original.
Try replacing the pattern ** => ([a-zA-Z])** with ** => £0.00 $1**
Basically searching for the context where there is null price and inserting zero pounds.
Hope this helps.
Good luck
Simply do this :
<?php
for($i=0;$i<count($my_array);$i++)
{
if($my_array[$i+1]value=="")
unset($my_array[$i])
}
?>
assume $a is your array.
function isPrice($str) {
return (substr($str, 0, 1) == '£');
}
$newA = array();
for($i=0;$i<count($a);$i++) {
if( isPrice($a[$i]) != isPrice($a[$i+1]) ){
$newA[] = $a[$i];
}
}
I wondering whether anyone has any good ideas on optimizing the following code. I have an multi-dimensional array ($List) as follows:
Array
(
[0] => Array
(
[id] => 1
[title] => A good read
[priority] => 10
)
[1] => Array
(
[id] => 2
[title] => A bad read
[priority] => 20
)
[2] => Array
(
[id] => 3
[title] => A good read
[priority] => 10
)
)
First I'm removing any entries that share the same title (no matter what the other values are) as follows:
$List_new = array();
foreach ($List as $val) {
$List_new[$val['title']] = $val;
}
$List = array_values($List_new);
Perfect. Then I'm reordering the array, first by the priority field and then id:
$sort_id = array();
$sort_priority = array();
foreach ($List as $key => $row) {
$sort_id[$key] = $row['id'];
$sort_priority[$key] = $row['priority'];
}
array_multisort($sort_priority, SORT_DESC, $sort_id, SORT_DESC, $List);
Both code blocks appear in a loop, hence the clearing of $sort_id and $sort_priority before reordering.
Is there a better way to do this - i.e. use the sorting process to remove duplicate title entries? This code block is being executed in a loop of up to 500,000 records and so any improvement would be welcome!
One loop, but a few extra function calls so I can't tell you how the Big O changes. One thing to note, the padding around numbers must be big enough to prevent overflow i.e. 2 = max 99 priorities and 6 = max 999,999 items.
$list_titles = array();
foreach($List as $val) {
if(isset($list_titles[$val['title']])) continue;
$list_titles[$val['title']] = true;
$List_new[str_pad($val['priority'], 2, 0, STR_PAD_LEFT).str_pad($val['id'], 6, 0, STR_PAD_LEFT)] = $val;
}
krsort($List_new);
Edit: made some minor modifications.
I have a array inside my PHP app that looks like this:
Array
(
[0] => Array
(
[name] => Name1
[language] => 1
)
[1] => Array
(
[name] => Name2
[language] => 1
)
)
How can I check that "language" with value 1 doesnt appear twice, as effectively as possible?
$dupe = 0;
foreach($yourarray as $key => $val) {
if(array_key_exists($seen, $val['language'])) {
// a duplicate exists!
$dupe = 1;
// could do other stuff here too if you want,
// like if you want to know the $key with the dupe
// if all you care about is whether or not any dupes
// exist, you could use a "break;" here to early-exit
// for efficiency. To find all dupes, don't use break.
}
$seen[$val['language']] = 1;
}
// If $dupe still = 0 here, then no duplicates exist.
Tried the PHP function array_unique?
(Read the comments/user contributed notes below, especially the one by regeda at inbox dot ru, who made a recursive function for multi-dimensional arrays)