Related
I have an xml file which has several elements (3rd and 4th level <def> as well as <ex> and <kref> ) that may repeat 1 or more times. When I convert this to json (and evenually yaml with yq), the multiple elements are arrays and understandably the single elments are not. How can I make jq/yq consisently render an element in an array even as a single item.
For brevity, I have included below sample xml and json data with the single elements as well as the desired json output where the single elements would be rendered as an array.
I have also tried converting with cat test.xml | xq '.xdxf.lexicon.ar[].def.def.def=[.def]' , and this seems to be starting to get me somewhere, but the resulting def array is empty. Any help on solving this much appreciated.
original xml:
<xdxf revision="034">
<lexicon>
<ar>
<k xml:lang="dyu">headword2</k>
<def freq="">
<co></co>
<def xml:lang="fr">
<def def-id="63491b7b-5d72-400c-95ee-f2e63f984832">
<gr>
<abbr></abbr>
</gr>
<co></co>
<def def-id="32dc09fd-212f-42da-9f45-7d0fa5258c49">
<deftext>
<dtrn>definition1</dtrn>
</deftext>
<ex type="exm" ex-id="dda450aa-121b-415f-92e4-63366fd78fb5">
<ex_orig></ex_orig>
<ex_tran></ex_tran>
</ex>
<etm></etm>
<categ></categ>
</def>
</def>
</def>
<sr>
<kref type="spv"></kref>
</sr>
</def>
</ar>
</lexicon>
</xdxf>
piped to jq .
{
"xdxf": {
"#revision": "034",
"lexicon": {
"ar": {
"k": {
"#xml:lang": "dyu",
"#text": "headword2"
},
"def": {
"#freq": "",
"co": null,
"def": {
"#xml:lang": "fr",
"def": {
"#def-id": "63491b7b-5d72-400c-95ee-f2e63f984832",
"gr": {
"abbr": null
},
"co": null,
"def": {
"#def-id": "32dc09fd-212f-42da-9f45-7d0fa5258c49",
"deftext": {
"dtrn": "definition1"
},
"ex": {
"#type": "exm",
"#ex-id": "dda450aa-121b-415f-92e4-63366fd78fb5",
"ex_orig": null,
"ex_tran": null
},
"etm": null,
"categ": null
}
}
},
"sr": {
"kref": {
"#type": "spv"
}
}
}
}
}
}
}
Desired output:
{
"xdxf": {
"#revision": "034",
"lexicon": {
"ar": {
"k": {
"#xml:lang": "dyu",
"#text": "headword2"
},
"def": {
"#freq": "",
"co": null,
"def": {
"#xml:lang": "fr",
"def": [
{
"#def-id": "63491b7b-5d72-400c-95ee-f2e63f984832",
"gr": {
"abbr": null
},
"co": null,
"def": [
{
"#def-id": "32dc09fd-212f-42da-9f45-7d0fa5258c49",
"deftext": {
"dtrn": "definition1"
},
"ex": [
{
"#type": "exm",
"#ex-id": "dda450aa-121b-415f-92e4-63366fd78fb5",
"ex_orig": null,
"ex_tran": null
}
],
"etm": null,
"categ": null
}
]
}
]
},
"sr": {
"kref": [
{
"#type": "spv"
}
]
}
}
}
}
}
}
My comment:
So you want every 3'th and 4'th def converted to an array with that object?
OP's comment:
Yes. Hopefully if I can see how that is done I can also see how to do convert ex and kref as well.
.xdxf.lexicon.ar.def.def.def |= [ (.def |= [ . ]) ]
First we select the 3'th def:
.xdxf.lexicon.ar.def.def.def
Then we use an update assignment to change it's value to an array, with the object inside
Normally we'd use |= [ . ] to do that
But since we need to perform the same action on the 4'th def, we apply the same logic inside the array to wrap the 4'th def:
(.def |= [ . ])
The () is used to ensure the |= only applies to that object.
Result JSON, visible in this online demo:
{
"xdxf": {
"#revision": "034",
"lexicon": {
"ar": {
"k": {
"#xml:lang": "dyu",
"#text": "headword2"
},
"def": {
"#freq": "",
"co": null,
"def": {
"#xml:lang": "fr",
"def": [
{
"#def-id": "63491b7b-5d72-400c-95ee-f2e63f984832",
"gr": {
"abbr": null
},
"co": null,
"def": [
{
"#def-id": "32dc09fd-212f-42da-9f45-7d0fa5258c49",
"deftext": {
"dtrn": "definition1"
},
"ex": {
"#type": "exm",
"#ex-id": "dda450aa-121b-415f-92e4-63366fd78fb5",
"ex_orig": null,
"ex_tran": null
},
"etm": null,
"categ": null
}
]
}
]
},
"sr": {
"kref": {
"#type": "spv"
}
}
}
}
}
}
}
I'm using api to get the response array, I'm trying to map the "id" under the "quiz_records" array but it returns undefined. I think that my code are correct.
This is my attempt.
array
"quizRemarks": [
{
"id": 160,
"user_id": 1,
"quiz_id": 18,
"module_id": 29,
"number_of_correct_answers": 2,
"created_at": "2021-10-15T03:52:52.000000Z",
"updated_at": "2021-10-15T03:52:52.000000Z",
"time_started": null,
"time_finished": null,
"remarks": 1,
"quiz_records": [
{
"id": 27,
"user_scores_id": 160,
"question_id": 2,
"user_answers": "DriverPH",
"remarks_correct_incorrect": "1",
"created_at": null,
"updated_at": null,
"question_text": "What is the name of this application?"
},
{
"id": 28,
"user_scores_id": 160,
"question_id": 2,
"user_answers": "Red",
"remarks_correct_incorrect": "1",
"created_at": null,
"updated_at": null,
"question_text": "What traffic light color tells you to stop before the intersection?"
}
]
}
]
ts
this.quiz_records = res['quizRemarks'].map(res => res['quiz_records'].id);
console.log(this.quiz_records);
quizRemarks is an array of objects containing an array of quiz_records. Try this to get a flat list of ids of quiz_records:
this.quiz_records = [];
this.quizRemarks.forEach(remark => {
this.quiz_records.push(...remark.quiz_records.map(rec => rec.id));
});
Below code will work----
this.quiz_records = res['quizRemarks'].map(res => res['quiz_records'].map(r => r.id));
You're truing to get an id property from an array, what it inpossible.
You can use nested map + flat() array method.
const result = res['quizRemarks'].map(remarks => {
return remarks['quiz_records'].map(record => record.id);
}).flat();
My object demoList:
demoList = [
{
id: 1,
validFrom: "2019-06-01T00:00:00",
validTo: "2020-06-17T00:00:00",
xxxM: 50,
xxxN: 2.2,
xxxQ45: 2,
xxxQ100: 1.65,
xxxQ125: null,
xxxQ150: null,
xxxQ250: null,
xxxQ300: null,
xxxQ500: null
},
{
id: 2,
validFrom: "2019-06-01T00:00:00",
validTo: "2020-06-17T00:00:00",
xxxM: 51,
xxxN: 22,
xxxQ45: 1.4,
xxxQ100: 1.65,
xxxQ125: null,
xxxQ150: 1.7,
xxxQ250: null,
xxxQ300: 0.15,
xxxQ500: null,
xxxQ700: 15.4
},
. . .
];
The array I want to obtain:
["M", "N", "45", "100", "150", "300", "700"]
I want an array of non-empty values starting with xxx and found in the demoList list.
How do I do this using angular?
You can .flatMap() your demoList objects to the Object.entries() of each object. You can keep only those entries which start with "xxx" and the value doesn't equal null by using .filter(). You can then map each key to either the last suffix number on the key or the last character of the key using .match(/\d+|\w$/g). You can then put this mapped array into a new Set() to remove any duplicates. Then you can spread ... this set into an array to convert the set back into an array:
const demoList = [{ id: 1, validFrom: "2019-06-01T00:00:00", validTo: "2020-06-17T00:00:00", xxxM: 50, xxxN: 2.2, xxxQ45: 2, xxxQ100: 1.65, xxxQ125: null, xxxQ150: null, xxxQ250: null, xxxQ300: null, xxxQ500: null }, { id: 2, validFrom: "2019-06-01T00:00:00", validTo: "2020-06-17T00:00:00", xxxM: 51, xxxN: 22, xxxQ45: 1.4, xxxQ100: 1.65, xxxQ125: null, xxxQ150: 1.7, xxxQ250: null, xxxQ300: 0.15, xxxQ500: null, xxxQ700: 15.4 } ];
const res = [...new Set(
demoList.flatMap(Object.entries)
.filter(([k, v]) => k.startsWith("xxx") && v !== null)
.map(([k]) => k.match(/\d+|\w$/g).pop())
)];
console.log(res);
I am trying to get these data below,
Table relations:
people(one) <---> (many) people_info (one) <---> (many) people_contact
in the following format,
people: {
p_id: 10,
p_price: 3.99,
people_info : [
{
pl_id: 3,
pl_state: 2,
pl_district: 6,
pl_latitude: 6.323434,
pl_longitude: 108.23499,
people_contact: [
{
plc_id: 2
},
{
plc_id: 1
}
]
},
{
pl_id: 2,
pl_state: 7,
pl_district: 12,
pl_latitude: 6.000434,
pl_longitude: 108.9910003,
people_contact: [
{
plc_id: 5
},
{
plc_id: 9
}
]
}
]
}
Currently with these controller codes,
class PeopleController extends Controller
{
public function actionPeople($params){
Yii::$app->response->format = Response::FORMAT_JSON;
....//some other codes//.....
$people= People::find()->select(['p_id', 'p_price'] )->where(['p_id' => $itemId])->one();
$info= PeopleContact::find()->with(['plPeople'])->asArray([])->all();
return array(
'people' => $people,
'info' => $info,
);
}
}
I got these,
"people": {
"p_id": "3",
"p_price": "32.42"
}, "locations": [{
"pl_id": "1",
"pl_people": "3",
"pl_title": "",
"pl_latitude": "6.16438700000000000000",
"pl_longitude": "102.28314649999993000000",
"pl_place": null,
"pl_premise": null,
"pl_street": "1",
"pl_area": "1",
"pl_postcode": "1",
"pl_district": "1",
"pl_state": "3",
"pl_country": 1,
"place": null,
"premise": null,
"street": null,
"area": null,
"postcode": null,
"district": null,
"state": null,
"country": "United Kingdom",
"contacts": [{
"plc_name": "joe",
"plc_phone": "123456",
"plc_email": null
}]
}]
}
How do I achieve it in the format mentioned at the top?
$output;
$people=People::find()->select(['p_id', 'p_price'] )->asArray()->all();
foreach($people as $person) {
$infos = PersonInfo::find()->where(['person_id' => $person->id])->asArray()->all();
foreach($infos as $info) {
$contacts = PersonContact::find()->where(['person_info_id' => $info->id])->asArray()->all();
foreach($contacts as $contact) {
$info['contacts'][] = $contact;
}
$person['info'][] = $info
}
$output['people'][] = $person
}
return $output;
You should loop through and fetch data like this: people > info > contact each next level relying on info fetched from the previous one. Then store it in the format you want such as demonstrated above.
This will output something like:
"people": [{
...
"info": [{
...
"contacts": [{
...
},{
...
}]
}]
},{
...
}]
I need to sort my json array in chronological order by it's created_at timestamps.
[{
"id": 1,
"message": "hello",
"company_id": 7,
"investor_id": 35,
"match_id": 2,
"created_at": "2015-07-01 12:56:34",
"updated_at": "2015-07-01 12:56:34"
}, {
"id": 2,
"message": "helloWorld",
"company_id": 7,
"investor_id": 35,
"match_id": 2,
"created_at": "2015-07-01 12:56:53",
"updated_at": "2015-07-01 12:56:53"
}, {
"id": 3,
"message": "sup",
"company_id": 7,
"investor_id": 35,
"match_id": 2,
"created_at": "2015-07-01 13:12:53",
"updated_at": "2015-07-01 13:12:53"
}, {
"id": 4,
"message": "sup",
"company_id": 7,
"investor_id": 35,
"match_id": 2,
"created_at": "2015-07-01 13:13:04",
"updated_at": "2015-07-01 13:13:04"
}, {
"id": 5,
"message": "sup",
"company_id": 7,
"investor_id": 35,
"match_id": 2,
"created_at": "2015-07-01 15:06:39",
"updated_at": "2015-07-01 15:06:39"
}, {
"id": 1,
"message": "yo yo ",
"investor_id": 35,
"company_id": 7,
"match_id": 2,
"created_at": "2015-07-01 22:09:36",
"updated_at": "-0001-11-30 00:00:00"
}, {
"id": 2,
"message": "sup nigga",
"investor_id": 35,
"company_id": 7,
"match_id": 2,
"created_at": "2015-07-01 14:00:00",
"updated_at": "-0001-11-30 00:00:00"
}]
Can anyone teach me how do i have tried many solutions in stackoverflow . Many of them says array cannot be used with "sortBy" .
This is part of my code :
$companyMessage = Company_Message::where('match_id','=',$match_id);
$investorMessage = Investor_Message::where('match_id','=',$match_id);
$message = array();
if($companyMessage->count()>0 || $investorMessage->count() > 0){
if($lastActivityDate == null ){
//load all messages before
if($companyMessage !=null){
foreach ($companyMessage->get() as $cm) {
array_push($message,$cm);
}
}
if($investorMessage !=null){
foreach($investorMessage->get() as $im){
array_push($message,$im);
}
}
return $message ;
}
I think you can do this using use a laravel database collection api instead of array and add item to collection and use sortBy method on collection,
$col = new \Illuminate\Database\Eloquent\Collection();
if ($companyMessage != null) {
foreach($companyMessage - > get() as $cm) {
$col->add($cm);
}
}
if ($investorMessage != null) {
foreach($investorMessage - > get() as $im) {
$col->add($im);
}
}
$col = $col->sortBy('created_at');
//return the json
return Response::json($jsonArr->toArray());
--- OR ----
Try this but i didn't tested it. If this works then this would be the best way to do this.
$companyMessage = Company_Message::where('match_id','=',$match_id);
$investorMessage = Investor_Message::where('match_id','=',$match_id);
//$companyMessage & $investorMessage both are laravel database collection,
// so you can use merge method on collection to merge these two collections.
$allResults = $companyMessage->merge($investorMessage);
//use laravel collection sort method to sort the collection by created_at
$allResults = $allResults->sortBy('created_at');
//return the json
return Response::json($allResults->toArray());
if($companyMessage !=null){
foreach ($companyMessage->get() as $cm) {
array_push($message,$cm);
}
}
if($investorMessage !=null){
foreach($investorMessage->get() as $im){
array_push($message,$im);
}
}
$message = array_values(array_sort($message, function ($value) {
return $value['created_at'];
}));
Hey man , i found a better solution to sort my array. The answer can be found at laravel docs
http://laravel.com/docs/5.1/helpers#method-array-sort