I have a very large object that I have to loop through to get a specific object.
I tried foreach($obj as $item), foreach($obj as $item => $value) and for in various configurations.
I want to get all the objects where the class is "table-responsive"
My Element looks like this (in JSON):
{
"dummy":"dummytext",
"children": [
{
"tag":null
},
{
"tag":"body",
"children": [
{
"class":"not_the_one"
},
{
"class":"table-responsive",
"html":"Gotcha!"
}
]
}
]
}
What I want to get as a result is:
{
"class":"table-responsive",
"html":"Gotcha!"
}
There could be more than one object that has the class of "table-responsive", so if there are multiple ones:
[
{
"class":"table-responsive",
"html":"Gotcha!"
},
{
"class":"table-responsive",
"html":"Gotcha!"
}
]
I was trying to accomplish a function that goes through all the elements and checks where the key of class equals "table-responsive" if so, push the object to an array. (array_push($result, $obj);) If the current element is an array, loop through it too. There can be a lot of dimensions so ideally the function calls itself. (recursive)
What you're after is a recursive reduce operation. Something that can
Iterate your elements
Capture any with the matching property, and
Perform the same operation on any children
// decode your JSON into an associative array
$data = json_decode($json, true);
// setup search parameters
$findKey = 'class';
$findValue = 'table-responsive';
// create a recursive reducer
$reducer = function($carry, $item) use (&$reducer, $findKey, $findValue) {
if (array_key_exists($findKey, $item) && $item[$findKey] === $findValue) {
// if this $item matches, add it to the $carry array
$carry[] = $item;
}
if (!empty($item['children'])) {
// this $item has children so dive deeper
return array_reduce($item['children'], $reducer, $carry);
}
// no children in this one so on to the next iteration
return $carry;
};
// Run the reducer at the top level.
// Note I've wrapped the root object in an array so the reducer can iterate it
$found = array_reduce([ $data ], $reducer, []);
// and display
echo json_encode($found, JSON_PRETTY_PRINT);
Demo ~ https://3v4l.org/6mWmC
Related
I am iterating trough array in php and I got result like:
It's a an array with DOM elements within a DOM Crawler library.
{
"data": [
{
"content": null,
"property": null
},
{
"content": "Build your communication strategy and unleash the effectiveness and efficiency of your storytelling",
"property": null
}
}
...
In my code:
$crawler = new Crawler(file_get_contents($url));
$items =$crawler->filter('meta');
$metaData = [];
foreach ($items as $item) {
$itemCrawler = new Crawler($item);
$metaData[] = [
'content' => $itemCrawler->eq(0)->attr('content'),
'property' => $itemCrawler->eq(0)->attr('property')
];
}
What I try to accomplish is to get rid of rows where both fields are NULL like first one (if there is one of the fileds, like second one than skip).
Tried with array_filter() but with no success.
return array_filter($metaData, 'strlen');
Not sure why the first answer wouldn't be accepted. It would be just a bit of tweaks to make it work.
In your loop
$itemCrawler = new Crawler($item);
$content = $itemCrawler->eq(0)->attr('content');
$property = $itemCrawler->eq(0)->attr('property');
if(!is_null($content) || !is_null($property)) {
$metaData[] = compact('content', 'property');
}
Or if you insist on using array_filter after getting the $metaData array
$filteredMetaData = array_filter($metaData, function($item) {
return !is_null($item['content']) || !is_null($item['property']);
});
Actually, array_filter() works for empty elements.
Even if the element values are blank, if the keys are there, the blank values will not be removed.
In the code:
$item Has two keys
So, add explicit conditions to check for blank elements and please modify the code like:
$data = [];
$items =$service->get('allData');
foreach ($items as $item) {
if (! empty($item['content']) && ! empty($item['property'])) {
$data[] = [
'content' => $item['content'],
'property' => $item['property]
];
}
}
What I want to achieve?
"product_attributes": [
{
"title": "Color",
"records": [
{
"attribute_name": "black",
"mpr": "100"
},
{
"attribute_name": "green",
"mpr": "200"
}
]
},
{
"title": "RAM",
"records": [
{
"attribute_name": "16GB",
"mpr": "10"
}
]
},
{
"title": "RAM",///remove this whole obeject
"records": []
}
]
what I have tried: I fetch whole attributes from the DB and then compare it to product attribute and made this format now the problem is when I start traversing its comparing result from all attribute which creates an empty object every time my if() condition fails.
how can I remove empty object having empty records array and reindex my final array?
here is my code :
$allattributes = DB::table('product_attributes')->where('subcategory_id', $product->subcat_id)->get(['attribute_title']);
$valuesnew = DB::table('current_product_attribute_values')->where('product_id', $product->id)->get();
$emptybool=false;
// checking for empty attributes
if ($valuesnew->count() > 0) {
// first foreach for 3 value
foreach ($allattributes as $name) {
//echo $name->attribute_title;
$boo = false;
// 40 record loop
$records = array();
foreach ($valuesnew as $compare) {
// if attibute title are same
if ($compare->attribute_title == $name->attribute_title) {
if ($boo == false) {
$titledata = $name->attribute_title;
$holddata['title'] = $titledata;
$boo = true;
}
$records[] = array("attribute_name" => $compare->attribute_value, "mpr" => $compare->attribute_mrp);
}
}
$holddata['records'] = $records;
$final[] = $holddata;
}
} else {
$final = array();
}
what i have tried:
foreach($final as $k=>$arr){
//$final=array_filter($arr,'count');
if(array_filter($arr,'count') || array_values(array_filter($arr))){
unset($arr[$i]);
}
$i++;
}
print_r($final);//failed
TEST CASE:
Fetching all attributes from the subcategories of which product belongs to.
fetching all product attributes from product attribute table
then comparing the title of all attributes with product attributes when found the same record I have put this in inside the array. so I achieve color=>red, black this type of structure instead of color=>red, color=>black
now the test case is when all attributes have 4 attributes color, size, ram, processor, and product having only two color and ram at this case my loop give me empty record as with the last title I want to remove that object having an empty record.
thanks in advance :)
**NEW TRY: **
foreach($final as $k=>$arr){
foreach($arr as $key=>$value){
$count=count($value);
if($count==0){
echo '<pre>';
echo ' am empty object remove me ';
'<br>';
unset($arr[$index]);//failed how can i remove this whole object from the main array
}
}
Someone already posted using filter its definitely the solution for you. You don't need to have get the data from the database to use a collection.
collect($productAttributesArray).filter(function($product){
return !empty($product->records);
}
you could try to filter the collection for rows the doesn't have record.
$withRecordsOnly = collect($product_attributes)->filter(function ($item) {
return !empty($item->records);
});
$product_attributes is the same array you have foreached. what this do is convert the array to a collection and filter out all object with a non empty records.
I have a array of various object, but I need turn this objects into unique object. Below I share my code.
$result = [];
$idiomas = Idioma::all()->toArray();
foreach ($idiomas as $lang) {
$result[] = [
$lang['palavra_chave'] => $lang[$id]
];
}
return response()->json($result);
reponse
[
{ "INICIAL": "Inicial"},{ "RELATORIOS": "Relatórios"},{ "FUNCIONARIO": "Funcionário"},{ "DATA": "Data"},{ "ANEXAR_IMAGEM": "Anexar imagem"},{ "DISCIPLINA": "Disciplina"}
]
But I need transform this objects into one, like this
[
{
"INICIAL": "Inicial",
"RELATORIOS": "Relatórios",
"FUNCIONARIO": "Funcionário",
"DATA": "Data",
"ANEXAR_IMAGEM": "Anexar imagem",
"DISCIPLINA": "Disciplina"
}
]
anyone can help me?
$idiomas = Idioma::all()->toArray();
if (count($idiomas)) {
//$result = new stdClass; # wouldn't work without base namespace
$result = new \stdClass;
foreach ($idiomas as $lang) {
$result->{$lang['palavra_chave']} = $lang[$id];
}
return response()->json([$result]);
}
// otherwise
Edit: #Tpojka's answer definitely looks more appropriate. Use the following one only if you can't change the way you retrieve data initially (I'm not familiar enough with Laravel).
The following should work:
// Take your initial JSON
$input = <<<JSON
[
{ "INICIAL": "Inicial"},{ "RELATORIOS": "Relatórios"},{ "FUNCIONARIO": "Funcionário"},{ "DATA": "Data"},{ "ANEXAR_IMAGEM": "Anexar imagem"},{ "DISCIPLINA": "Disciplina"}
]
JSON;
// Transform it into a PHP array
$input_as_array = json_decode($input);
// Reduce it into an associative array
$associative_array = array_reduce($input_as_array, function($associative_array, $item) {
return $associative_array += (array)$item;
}, []);
// Encode it back into JSON, as an array
$result = json_encode([$associative_array], JSON_PRETTY_PRINT);
I need to parse JSON which looks like this:
{
"mdfId":"282088127",
"mdfConcept":"ME 3400EG-12CS-M Switch",
"children":[
{
"mdfId":"007",
"mdfConcept":"Another item",
"children": [
// many more here
]
},
{
"mdfId":"008",
"mdfConcept":"Another one",
"children": [
{
"mdfId":"010",
"mdfConcept":"What I'm looking for!",
"children": [] // no children
}
]
},
// many more here
]
},
This is a recursive structure in which every element has mdfId, mdfConcept and children keys.
Say I need to find node with ID=010 within this structure. I don't know at which level it lies (e.g. it can be on top level, or several children nodes below).
My current approach is:
$mdfId = '010'; // what I'm loking for
foreach ($jsonResponse as $category) {
while (true) {
if ($category['mdfId'] == $mdfId) {
// we found it!
$categoryDevices[$mdfId] = $category['children'];
break 2;
}
if (!empty($category['children'])) {
next_cat:
if (is_null($category['children'])) {
break;
}
$category = array_shift($category['children']);
continue;
}
if (empty($category['children'])) {
goto next_cat;
}
}
}
But current approach misses some cases. How can I optimize this recursive loop so it checks all nodes on same level and each one accesible through any number of children keys?
An embarrassing feature of your JSON object is that, while each children member is an array of the "child" structure, the top level one is the object itself, so it's an obstacle to a really recursive approach.
We might workaround by turning the source JSON object into the same structure as nested levels, i.e.:
having $jsonResponse as original object
use ['children' => $jsonResponse] instead
This way, it should work with something like this:
$mdfId = '010'; // what I'm loking for
if ($result = look4id(['children' => $jsonResponse], $mdfId) {
$categoryDevices[$mdfId] = $result;
}
function look4id($source, $id) {
foreach ($source as $child) {
if ($child['mdfId'] == $id) {
return $source['children'];
} else {
if ($source['children']) {
return look4id($source['children'], $id);
}
}
}
}
So basically I wrote a function that didn't return anything, but rather populated a variable from arguments.
function findRecursiveArrayNodeById($id, $array, &$node) {
foreach ($array as $child) {
if (isset($child['mdfId']) && $child['mdfId'] == $id) {
$node = $child;
return;
}
if (!empty($child['children'])) {
findRecursiveArrayNodeById($id, $child['children'], $node);
}
}
}
Usage as follows:
$result = false;
findRecursiveArrayNodeById($mdfId, $category_json, $result);
if (!$result) {
println("did not find {$mdfId}");
continue;
}
I have one array having multiple objects (say 3 Objects), each having 3 "Key-Value" pairs.
$PredefinedResult is something like this :
[
{
"EffectiveStatusId":0,
"EffectiveStatus":"abc",
"RecordCount":0
},
{
"EffectiveStatusId":0,
"EffectiveStatus":"def",
"RecordCount":0
},
{
"EffectiveStatusId":0,
"EffectiveStatus":"ghi",
"RecordCount":0
}
]
I have another array of objects named $MainResult with values like :
[
{
"EffectiveStatusId":1,
"EffectiveStatus":"abc",
"RecordCount":7
},
{
"EffectiveStatusId":6,
"EffectiveStatus":"def",
"RecordCount":91
}
]
Expected Result :
I want to replace the similar objects inside $PredefinedResult with the objects from $MainResult and want result like this :
[
{
"EffectiveStatusId":1,
"EffectiveStatus":"abc",
"RecordCount":7
},
{
"EffectiveStatusId":6,
"EffectiveStatus":"def",
"RecordCount":91
},
{
"EffectiveStatusId":0,
"EffectiveStatus":"ghi",
"RecordCount":0
}
]
What I tried :
I tried with this code but it's not giving me the desired result.
$FinalResult = array_replace($PredefineResult,$MainResult);
Can anyone help me on how to get the Expected Result as mentioned above ?
There's no "built-in" function for this. You're gonna have to loop and compare each manually. array_map seems like an OK choice here:
$PredefinedResult = array_map(function($a) use($MainResult){
foreach($MainResult as $data){
if($a->EffectiveStatus === $data->EffectiveStatus){
return $data;
}
}
return $a;
}, $PredefinedResult);
DEMO: http://codepad.viper-7.com/OHBQK8
Iterate through the array and manual compare the values as follows.
$res = array();
foreach ($PredefineResult as $result){
foreach ($MainResult as $mresult){
if(($result->EffectiveStatus == $mresult->EffectiveStatus) && $mresult->RecordCount!=0){
$res[] = $mresult;
}else $res[] = $result;
}
}
print_r($res);