In my application I have a response like this:
{
"items": [
{
"id": 10,
"field": "foo"
},
{
"id": 20,
"field": "bar"
}
]
}
I need to test the content of items and validate each id.
I've tried many solutions but no one works, for example (this is just a kind of pseudo-code):
assertJson(fn (AssertableJson $json) =>
$json->where('items.*.id', [10, 20])
)
Is there a way to use a wildcard to pick every ID and validate using an array?
You can use array_filter:
$idArray = [10, 20];
$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object
// Filter the items for items that aren't in the ID list
$invalidItems = array_filter($items, function ($el) {
// If the item has an id which isn't in the array, return true
return !in_array($el["id"], $idArray);
});
// This returns true if we found 0 items with IDs not in the ID list
return $invalidItems == [];
You can similarly use array_map to simplify your array, then compare it to your ID array:
$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object
$outIdArray = array_map(function($el) {
return $el["id"];
}, $items);
// Compare $outIdArray to [10, 20]
Not tested yet but below should work.
We attach an each on each child element under items and add a callback to where on that id key of each child.
<?php
assertJson(fn (AssertableJson $json) =>
$json->each('items', fn (AssertableJson $childJson) =>
$childJson->where('id', fn($idVal) =>
in_array($idVal, [10,20])
)
)
)
Related
I have a list object like:
"my_list":
[
{
"id": 1,
"name": A
},
{
"id": 2,
"name": B
},
]
I want to convert to key - value like:
my_list = {
1: 'A',
2: 'B'
}
How can I do that? Does anyone have solution?
Please help, thanks!
Cast it to Laravel collection and use mapWithKeys()
$keyed = collect($my_list)->mapWithKeys(function ($item) {
return [$item['id'] => $item['name']];
});
If you need to convert it to object then:
$keyed = (object)$keyed->toArray();
You can use array helper methods combines with array_combine():
(object)array_combine(Arr::pluck($my_list, 'id'), Arr::pluck($my_list, 'name'));
I have this:
$merge = parents::with('children')->get();
which gives me:
[{
"id":1,
"parent_name":"Robert",
"children":[{
"id":5,"name":"Susan","dob":"2010-11-11"},
{"id":7,"name":"Tony","dob":"2014-12-10"}]
}]
I want to add age into children with expected output like this:
[{
"id":1,
"parent_name":"Robert",
"children":[{
"id":5,"name":"Susan","dob":"2010-11-11","age":"10"},
{"id":7,"name":"Tony","dob":"2014-12-10","age":"7"}]
}]
I tried this:
return $merge->map(function ($detail) {
$detail->age = \Carbon\Carbon::parse($detail->dob)->diffInYears();
return $detail;});
it gives me:
[{
"id":1,
"parent_name":"Robert",
"age":0,
"children":[{
"id":5,"name":"Susan","dob":"2010-11-11"},
{"id":7,"name":"Tony","dob":"2014-12-10"}]
}]
I tried:
return $merge->children->map(function ($detail) {
$detail->age = \Carbon\Carbon::parse($detail->dob)->diffInYears();
return $detail;});
It returns
Property [children] does not exist on this collection instance.
Is there any simple way to achieve my goal?
loop on each parent and its children and add new age property
$data = parents::with('children')->get();
$data->each(function($parent) {
$parent->children->each(function($child){
$child->age = \Carbon\Carbon::parse($child->dob)->diffInYears();
});
});
what I suggest and I think is better is to add an attribute to your Model like so:
class modelName extends Model{
protected $append = ['age'];
public function getAgeAttribute(){
return \Carbon\Carbon::parse($this->dob)->diffInYears();
}
}
Loop through parents and its children. Eventually calculate the age and put the value to array.
$merge->map(function ($mer) {
$mer->children->map(function ($child) {
$age = \Carbon\Carbon::parse($child->dob)->diffInYears();
$child->age = $age;
});
});
Is there a way in RxJs to perform two api calls where the second requires data from the first and return a combined result as a stream? What I'm trying to do is call the facebook API to get a list of groups and the cover image in various sizes. Facebook returns something like this:
// call to facebook /1234 to get the group 1234, cover object has an
// image in it, but only one size
{ id: '1234', cover: { id: '9999' } }
// call to facebook /9999 to get the image 9999 with an array
// with multiple sizes, omitted for simplicity
{ images: [ <image1>, <image2>, ... ] }
// desired result:
{ id: '1234', images: [ <image1>, <image2>, ... ] }
So I have this:
var result = undefined;
rxGroup = fbService.observe('/1234');
rxGroup.subscribe(group => {
rxImage = fbService.observe(`/${group.cover.id}`);
rxImage.subscribe(images => {
group.images = y;
result = group;
}
}
I want to create a method that accepts a group id and returns an Observable that will have the combined group + images (result here) in the stream. I know I can create my own observable and call the next() function in there where I set 'result' above, but I'm thinking there has to be an rx-way to do this. select/map lets me transform, but I don't know how to shoe-in the results from another call. when/and/then seems promising, but also doesn't look like it supports something like that. I could map and return an observable, but the caller would then have to do two subscribes.
Looks like flatMap is the way to go (fiddle). It is called like subscribe and gives you a value from a stream. You return an observable from that and it outputs the values from all the created observables (one for for each element in the base stream) into the resulting stream.
var sourceGroup = { // result of calling api /1234
id: '1234',
cover: {
id: '9999'
}
};
var sourceCover = { // result of calling api /9999
id: '9999',
images: [{
src: 'image1x80.png'
}, {
src: 'image1x320.png'
}]
};
var rxGroup = Rx.Observable.just(sourceGroup);
var rxCombined = rxGroup.flatMap(group =>
Rx.Observable.just(sourceCover)
.map(images => ({
id: group.id,
images: images.images
}))
)
rxCombined.subscribe(x =>
console.log(JSON.stringify(x, null, 2)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.min.js"></script>
Result:
{
"id": "1234",
"images": [
{
"src": "image1x80.png"
},
{
"src": "image1x320.png"
}
]
}
You should use concatMap instead of flatMap, it will preserve the order of the source emissions.
I have an object like this:
Company {
Enable bool
Pro bool
Type int
Categories []int
}
So, how can I make query to retrieve all objects that Enable=true, Categories contains, for example, "1" and order by Pro and Type.
I have more than 200,000 records so I have to to that efficient with indexes.
I try to use this:
r.db("test_main").table("companies").indexCreate("ListAll", function(comp) {
return comp("Categories").map(function(cat) {
return [ comp("Pro"), comp("CompType"), comp("Enabled"), cat ];
});
}, {multi: true})
r.db("test_main").table("companies").between([false, 0, true, 1],
[r.maxval, r.maxval, true, 1], {index:"ListAll"}).orderBy({index:r.desc("ListAll")}).limit(100)
But categories doesn't match.
I am developing a web app using Codeigniter and MongoDB.
I am trying to get the map reduce to work.
I got a file document with the below structure. I would like to do a map reduce to
check how many times each tag is being used and output it to the collection files.tags.
{
"_id": {
"$id": "4f26f21f09ab66c1030d0000e"
},
"basic": {
"name": "The filename"
},
"tags": [
"lorry",
"house",
"car",
"bicycle"
],
"updated_at": "2012-02-09 11:08:03"
}
I tried this map reduce command but it does not count each individual tag:
$map = new MongoCode ("function() {
emit({tags: this.tags}, {count: 1});
}");
$reduce = new MongoCode ("function( key , values ) {
var count = 0;
values.forEach(function(v) {
count += v['count'];
});
return {count: count};
}");
$this->mongo_db->command (array (
"mapreduce" => "files",
"map" => $map,
"reduce" => $reduce,
"out" => "files.tags"
)
);
Change your Map function to:
function map(){
if(!this.tags) return;
this.tags.forEach(function(tag){
emit(tag, {count: 1});
});
}
Yea, this map/reduce simply calculate total count of tags.
In mongodb cookbook there is example you are looking for.
You have to emit each tag instead of entire collection of tags:
map = function() {
if (!this.tags) {
return;
}
for (index in this.tags) {
emit(this.tags[index], 1);
}
}
You'll need to call emit once for each tag in the input documents.
MongoDB documentation for example says:
A map function calls emit(key,value) any
number of times to feed data to the reducer. In most cases you will
emit once per input document, but in some cases such as counting tags,
a given document may have one, many, or even zero tags.