I have the following complex array
[
{
label: "Country1",
metrics: [
{
label: "xyz",
metric: "xyz",
value: 234184
},
{
label: "abc",
metric: "abc",
value: 145678
}
]
},
{
label: "Country2",
metrics: [
{
label: "xyz",
metric: "xyz",
value: 123456
},
{
label: "abc",
metric: "abc",
value: 456789
}
]
},
{
label: "Country3",
metrics: [
{
label: "xyz",
metric: "xyz",
value: 62389
},
{
label: "abc",
metric: "abc",
value: 4964738
}
]
}
]
I need to convert it to the following simple array wherein from the metrics sub array the values for label and value becomes a key value pair.
[
{label: “Country1”, xyz: 234184, abc: 145678},
{label: “Country2”, xyz: 123456, abc: 456789},
{label: “Country3”, xyz: 62389, abc: 4964738}
]
Can this conversion happen using lodash?
There might be a better / cleaner way to do this, but this is what I could come up with in just a few minutes.
const inputData = [
{
label: "Country1",
metrics: [
{
label: "xyz",
metric: "xyz",
value: 234184
},
{
label: "abc",
metric: "abc",
value: 145678
}
]
},
{
label: "Country2",
metrics: [
{
label: "xyz",
metric: "xyz",
value: 123456
},
{
label: "abc",
metric: "abc",
value: 456789
}
]
},
{
label: "Country3",
metrics: [
{
label: "xyz",
metric: "xyz",
value: 62389
},
{
label: "abc",
metric: "abc",
value: 4964738
}
]
}
];
const newData = _.map(inputData, first => {
const additional = _.map(first.metrics, metric => [metric.label, metric.value]);
return _.assign({}, _.fromPairs(additional), {
label: first.label
});
});
console.log(newData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
Here's a solution that doesn't require lodash, you simply have to take advantage of using Array#map to transform each item in the top-level array, and then use Array#reduce to transform the object and get the rest of the properties you need. The methodology used below takes advantage of the following ES6 features:
Destructuring assignment to extract the properties from the Array#map and Array#reduce callbacks.
Spread Syntax to combine properties of objects from different sources.
Computed property names for a dynamic property name in an object.
var result = data.map(({ label, metrics }) =>
metrics.reduce(
(result, { metric, value }) => ({ ...result, [metric]: value }),
{ label }
)
);
var data = [{label:"Country1",metrics:[{label:"xyz",metric:"xyz",value:234184},{label:"abc",metric:"abc",value:145678}]},{label:"Country2",metrics:[{label:"xyz",metric:"xyz",value:123456},{label:"abc",metric:"abc",value:456789}]},{label:"Country3",metrics:[{label:"xyz",metric:"xyz",value:62389},{label:"abc",metric:"abc",value:4964738}]}];
var result = data.map(({ label, metrics }) =>
metrics.reduce(
(result, { metric, value }) => ({ ...result, [metric]: value }),
{ label }
)
);
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
Related
I have created one saved search, and I am fetching data from by suitescript and here is the demo data response which I am getting this result from saved search response
{
"results": [
{
"recordType": "itemreceipt",
"id": "2572118",
"values": {
"trandate": "2021-3-25",
"type": [
{
"value": "ItemRcpt",
"text": "Item Receipt"
}
],
"tranid": "RCV123",
"salesrep": [
{
"value": "16018",
"text": "MXZ"
}
],
"entity": [
{
"value": "16993",
"text": "ABC"
}
],
"memo": "",
"amount": "1218.00",
"location": [
{
"value": "1",
"text": "XYZ"
}
],
"inventoryDetail.inventorynumber": [
{
"value": "10504",
"text": "3566044578"
}
]
}
}
]
}
AND I AM USING BELOW CODE TO MAKE FILTER BY inventoryDetail.inventorynumber FIELD WHICH IT MENTIONED IN SAVED SEARCH RESPONSE DATA AND IT THROWS ERROR "An nlobjSearchFilter contains invalid search criteria: inventoryDetail.issueinventorynumber."
but if I used tranid instead of inventoryDetail.issueinventorynumber then it does not throw the error, why I can not filter by inventoryDetail.inventorynumber I am trying since two days but no luck, please help me guys
/**
* #NApiVersion 2.1
* #NScriptType Restlet
* #NModuleScope Public
*/
/*
------------------------------------------------------------------------------------------
Script Information
------------------------------------------------------------------------------------------
Name:
Saved Search API
ID:
_saved_search_api
*/
var
log,
search,
response = new Object();
define( [ 'N/log', 'N/search' ], main );
function main( logModule, searchModule ) {
log = logModule;
search = searchModule;
return { post: postProcess }
}
function postProcess( request ) {
try {
var searchObj = search.load( { id: 1234 } );//saved search id
// Copy the filters from rs into defaultFilters.
var defaultFilters = searchObj.filters;
// below code works
defaultFilters.push(search.createFilter({
name: "tranid",
operator: search.Operator.IS,
values: ["RCV123"]
}));
// but this code does not works and it throws error "An nlobjSearchFilter contains invalid search criteria: inventoryDetail.issueinventorynumber."
/*defaultFilters.push(search.createFilter({
name: "inventoryDetail.inventorynumber",
operator: search.Operator.IS,
values: ["3566044578"]
}));*/
searchObj.filters = defaultFilters;
searchObj.filters = defaultFilters;
response.results = [];
var resultSet = searchObj.run();
var start = 0;
var results = [];
do {
results = resultSet.getRange( { start: start, end: start + 1000 } );
start += 1000;
response.results = response.results.concat( results ) ;
response.count =results.length;
} while ( results.length );
return response;
} catch( e ) {
log.debug( { 'title': 'error', 'details': e } );
return { 'error': { 'type': e.type, 'name': e.name, 'message': e.message } }
}
}
issueinventorynumber is not a valid search Column on the inventorydetail record. You are probably looking for inventorydetail.inventorynumber. You can reference the Search Columns section for the Inventory Detail within the Records Browser.
Be aware that Search Column names are not always the same as the field ID in the UI, as is the case here. The ID in the UI is issueinventorynumber, while the ID for the Search Column is inventorynumber.
Finally I found the solution
var transactionSearchObj = search.create({
type: "transaction",
filters:
[
["formulatext: {inventorydetail.inventorynumber}","contains","30124578547"]
],
columns:
[
search.createColumn({name: "trandate", label: "Date"}),
search.createColumn({name: "type", label: "Type"}),
search.createColumn({name: "tranid", label: "Document Number"}),
search.createColumn({name: "salesrep", label: "Sales Rep"}),
search.createColumn({name: "memo", label: "Memo"}),
search.createColumn({name: "amount", label: "Amount"}),
search.createColumn({name: "location", label: "Location"}),
search.createColumn({
name: "itemid",
join: "item",
label: "Name"
}),
search.createColumn({
name: "inventorynumber",
join: "inventoryDetail",
label: " Number"
})
]
});
return transactionSearchObj.run().getRange( { start: 0, end: 0 + 1000 } );
The following query:
Paginate(Documents(Collection("backyard"))),
Lambda(
"f",
Let(
{
backyard: Get(Var("f")),
user: Get(Select(["data", "user"], Var("backyard")))
},
{
backyard: Var("backyard"),
user: Var("user")
}
)
)
)
results to:
{
data: [
{
backyard: {
ref: Ref(Collection("backyard"), "333719283470172352"),
ts: 1654518359560000,
data: {
user: Ref(Collection("user"), "333718599460978887"),
product: "15358",
date: "2022-06-06",
counter: "1"
}
},
user: {
ref: Ref(Collection("user"), "333718599460978887"),
ts: 1654517707220000,
data: {
email: "<email>",
name: "Paolo"
}
}
},
{
backyard: {
ref: Ref(Collection("backyard"), "333747850716381384"),
ts: 1654545603400000,
data: {
user: Ref(Collection("user"), "333718599460978887"),
product: "15358",
date: "2022-06-08",
counter: "4"
}
},
user: {
ref: Ref(Collection("user"), "333718599460978887"),
ts: 1654517707220000,
data: {
email: "<email>",
name: "Paolo"
}
}
}
]
}
How can I filter backyard by date without losing the nested users?
I tried:
Map(
Paginate(Range(Match(Index("backyard_by_date")), "2022-05-08", "2022-06-08")),
Lambda(
"f",
Let(
{
backyard: Get(Var("f")),
user: Get(Select(["data", "user"], Var("backyard")))
},
{
backyard: Var("backyard"),
user: Var("user")
}
)
)
)
However, the resultset is an empty array and the following already returns an empty array:
Paginate(Range(Match(Index("backyard_by_date")), "2022-05-08", "2022-06-08"))
My index:
{
name: "backyard_by_date",
unique: false,
serialized: true,
source: "backyard"
}
Maybe I have to adjust my index? The following helped me a lot:
How to get nested documents in FaunaDB?
How to Get Data from two collection in faunadb
how to join collections in faunadb?
Your index definition is missing details. Once that gets fixed, everything else you were doing is exactly right.
In your provided index, there are no terms or values specified, which makes the backyard_by_date index a "collection" index: it only records the references of every document in the collection. In this way, it is functionally equivalent to using the Documents function but incurs additional write operations as documents are created or updated within the backyard collection.
To make your query work, you should delete your existing index and (after 60 seconds) redefine it like this:
CreateIndex({
name: "backyard_by_date",
source: Collection("backyard"),
values: [
{field: ["data", "date"]},
{field: ["ref"]}
]
})
That definition configures the index to return the date field and the reference for every document.
Let's confirm that the index returns what we expect:
> Paginate(Match(Index("backyard_by_date")))
{
data: [
[ '2022-06-06', Ref(Collection("backyard"), "333719283470172352") ],
[ '2022-06-08', Ref(Collection("backyard"), "333747850716381384") ]
]
}
Placing the date field's value first means that we can use it effectively in Range:
> Paginate(Range(Match(Index("backyard_by_date")), "2022-05-08", "2022-06-08"))
{
data: [
[ '2022-06-06', Ref(Collection("backyard"), "333719283470172352") ],
[ '2022-06-08', Ref(Collection("backyard"), "333747850716381384") ]
]
}
And to verify that Range is working as expected:
> Paginate(Range(Match(Index("backyard_by_date")), "2022-06-07", "2022-06-08"))
{
data: [
[ '2022-06-08', Ref(Collection("backyard"), "333747850716381384") ]
]
}
Now that we know the index is working correctly, your filter query needs a few adjustments:
> Map(
Paginate(
Range(Match(Index("backyard_by_date")), "2022-05-08", "2022-06-08")
),
Lambda(
["date", "ref"],
Let(
{
backyard: Get(Var("ref")),
user: Get(Select(["data", "user"], Var("backyard")))
},
{
backyard: Var("backyard"),
user: Var("user")
}
)
)
)
{
data: [
{
backyard: {
ref: Ref(Collection("backyard"), "333719283470172352"),
ts: 1657918078190000,
data: {
user: Ref(Collection("user"), "333718599460978887"),
product: '15358',
date: '2022-06-06',
counter: '1'
}
},
user: {
ref: Ref(Collection("user"), "333718599460978887"),
ts: 1657918123870000,
data: { name: 'Paolo', email: '<email>' }
}
},
{
backyard: {
ref: Ref(Collection("backyard"), "333747850716381384"),
ts: 1657918172850000,
data: {
user: Ref(Collection("user"), "333718599460978887"),
product: '15358',
date: '2022-06-08',
counter: '4'
}
},
user: {
ref: Ref(Collection("user"), "333718599460978887"),
ts: 1657918123870000,
data: { name: 'Paolo', email: '<email>' }
}
}
]
}
Since the index returns a date string and a reference, the Lambda inside the Map has to accept those values as arguments. Aside from renaming f to ref, the rest of your query is unchanged.
This question already has an answer here:
Asserting and using conditions for an array response in Karate
(1 answer)
Closed 2 years ago.
I want to loop through below nested json structure and want to update all the required fields, however I could achieve this through typescript but want to do this in karate JS, I do not see any examples how to nested for each works.
I want to update 26 periods data(here for readability i used 3), based on index I want to update period field, i.e. if(index == key), these 26 periods are under each car attribute.(NOTe: you again have multiple cars and multiple car attributes and each car attribute you have 26 periods data)
I cannot use this Karate - Match two dynamic responses only when you have single array list and have less data
[
{
"cars": [
{
"name": "car 1",
"periodsData": [
{
"period": "5ed73ed31a775d1ab0c9fb5c",
"index": 1
},
{
"period": "5ed73ed31a775d1ab0c9fb5d",
"index": 2
},
{
"period": "5ed73ed31a775d1ab0c9fb5e",
"index": 3
}
]
},
{
"name": "car 2",
"periodsData": [
{
"period": "5ed73ed31a775d1ab0c9fb5c",
"index": 1
},
{
"period": "5ed73ed31a775d1ab0c9fb5d",
"index": 2
},
{
"period": "5ed73ed31a775d1ab0c9fb5e",
"index": 3
}
]
},
{
"name": "car 3",
"periodsData": [
{
"period": "5ed73ed31a775d1ab0c9fb5c",
"index": 1
},
{
"period": "5ed73ed31a775d1ab0c9fb5d",
"index": 2
},
{
"period": "5ed73ed31a775d1ab0c9fb5e",
"index": 3
}
]
}
],
"totalPeriodEprps": [
{
"period": "5ed73ed31a775d1ab0c9fb5c",
"index": 1
},
{
"period": "5ed73ed31a775d1ab0c9fb5d",
"index": 2
},
{
"period": "5ed73ed31a775d1ab0c9fb5e",
"index": 3
}
]
}
carId ="dfd"
]
This above array repeats
Type script code
//periods is a map of index and values
async modifyCarsData(mid, id, periods, campaignData) {
//carData is a json file
carData.forEach(element => {
element.carId= id;
// Update all egrp periods
element.totalPeriodEGRPs.forEach(eGrpPeriod => {
// egrprd.period =
if (periods.size === element.totalPeriodEGRPs.length) {
periods.forEach((value, key) => {
if (key === eGrpPeriod.index.toString()) {
eGrpPeriod.period = value;
return true;
}
});
}
});
element.cars.forEach(carCell => {
// Logic for updating periods data
carCell .periodsData.forEach(periodAttribute => {
if (periods.size === carCell.periodsData.length) {
periods.forEach((value, key) => {
if (key === periodAttribute.index.toString()) {
periodAttribute.period = value;
return true;
}
});
}
});
});
});
Don't think of this as an update, but as a transform. I'm not using your example because it is un-necessarily complicated. Here is a simpler example that gives you all the concepts you need:
* def data = [{ name: 'one', periods: [{ index: 1, value: 'a' },{ index: 2, value: 'b' }]}, { name: 'two', periods: [{ index: 1, value: 'c' },{ index: 2, value: 'd' }]}]
* def fnPeriod = function(x){ x.value = x.value + x.index; return x }
* def fnData = function(x){ return { name: x.name, periods: karate.map(x.periods, fnPeriod) } }
* def converted = karate.map(data, fnData)
* print converted
Which prints:
[
{
"name": "one",
"periods": [
{
"index": 1,
"value": "a1"
},
{
"index": 2,
"value": "b2"
}
]
},
{
"name": "two",
"periods": [
{
"index": 1,
"value": "c1"
},
{
"index": 2,
"value": "d2"
}
]
}
]
If this doesn't work for you, please look for another tool. Karate is designed for testing and assertions, not doing what you normally do in programming languages. And I suspect that you have fallen into the trap of writing "over-smart tests", so please read this: https://stackoverflow.com/a/54126724/143475
Also refer: https://stackoverflow.com/a/53120851/143475
I am struggling to get an Index by Date to work with a Range.
I have this collection called orders:
CreateCollection({name: "orders"})
And I have these sample entries, with one attribute called mydate. As you see it is just a string. And I do need to create the date as a string since in my DB we already have around 12K records with dates like that so I cant just start using the Date() to create them.
Create(Collection("orders"), {data: {"mydate": "2020-07-10"}})
Create(Collection("orders"), {data: {"mydate": "2020-07-11"}})
Create(Collection("orders"), {data: {"mydate": "2020-07-12"}})
I have created this index that computes the date to and actual Date object
CreateIndex({
name: "orders_by_my_date",
source: [
{
collection: Collection("orders"),
fields: {
date: Query(Lambda("order", Date(Select(["data", "mydate"], Var("order"))))),
},
},
],
terms: [
{
binding: "date",
},
],
});
If I try to fetch a single date the index works.
// this works
Paginate(
Match(Index("orders_by_my_date"), Date("2020-07-10"))
);
// ---
{
data: [Ref(Collection("orders"), "278496072502870530")]
}
But when I try to get a Range it never finds data.
// This does NOT work :(
Paginate(
Range(Match(Index("orders_by_my_date")), Date("2020-07-09"), Date("2020-07-15"))
);
// ---
{
data: []
}
Why the index does not work with a Range?
Range operates on the values of an index, not on the terms.
See: https://docs.fauna.com/fauna/current/api/fql/functions/range?lang=javascript
You need to change your index definition to:
CreateIndex({
name: "orders_by_my_date",
source: [
{
collection: Collection("orders"),
fields: {
date: Query(Lambda("order", Date(Select(["data", "mydate"], Var("order"))))),
},
},
],
values: [
{ binding: "date" },
{ field: ["ref"] },
],
})
Then you can get the results that you expect:
> Paginate(Range(Match(Index('orders')), Date('2020-07-11'), Date('2020-07-15')))
{
data: [
[
Date("2020-07-11"),
Ref(Collection("orders"), "278586211497411072")
],
[
Date("2020-07-12"),
Ref(Collection("orders"), "278586213229658624")
],
[
Date("2020-07-13"),
Ref(Collection("orders"), "278586215000703488")
],
[
Date("2020-07-14"),
Ref(Collection("orders"), "278586216887091712")
],
[
Date("2020-07-15"),
Ref(Collection("orders"), "278586218585784832")
]
]
}
Another alternative is to use a filter with a lambda expression to validate which values you want
Filter(
Paginate(Documents(Collection('orders'))),
Lambda('order',
And(
GTE(Select(['data', 'mydate'], Var('order')), '2020-07-09'),
LTE(Select(['data', 'mydate'], Var('order')), '2020-07-15')
)
)
)
You can update the conditions as you need
I believe this will work with the strings you have already
There are some mistakes here, first of all, you have to create documents that way:
Create(Collection("orders"), {data: {"mydate": ToDate("2020-07-10")}})
The index has to be created like this:
CreateIndex(
{
name: "orders_by_my_date",
source: Collection("orders"),
values:[{field:['data','mydate']},{field:['ref']}]
}
)
and finally, you can query your index and range:
Paginate(Range(Match('orders_by_my_date'),[Date("2020-07-09")], [Date("2020-07-15")]))
{ data:
[ [ Date("2020-07-10"),
Ref(Collection("orders"), "278532030954734085") ],
[ Date("2020-07-11"),
Ref(Collection("orders"), "278532033804763655") ],
[ Date("2020-07-12"),
Ref(Collection("orders"), "278532036737630725") ] ] }
or if you want to get the full doc:
Map(Paginate(Range(Match('orders_by_my_date'),[Date("2020-07-09")], [Date("2020-07-15")])),Lambda(['date','ref'],Get(Var('ref'))))
{ data:
[ { ref: Ref(Collection("orders"), "278532030954734085"),
ts: 1601887694290000,
data: { mydate: Date("2020-07-10") } },
{ ref: Ref(Collection("orders"), "278532033804763655"),
ts: 1601887697015000,
data: { mydate: Date("2020-07-11") } },
{ ref: Ref(Collection("orders"), "278532036737630725"),
ts: 1601887699800000,
data: { mydate: Date("2020-07-12") } } ] }
I am new to lodash.
I am having trouble filtering with lodash. I have a deep nested json object that I want to filter if productName = 'Lotto' and the board selectionMethod = "AUTOPICK"
When I try the solution below, it returns all results instead of filtering. I have tried multiple ways to do this but I always get all results returning.
Could anyone offer a suggestion?
var results = {
"buyTicketDetails": {
"result": 0,
"message": "Success",
"product": [
{
"productName": "Lotto",
"displayPromoMessage": false,
"drawDetails": [
{
"drawTypeDescription": "REGULAR DRAW",
"drawAttribute": "EVENING",
"drawStartDate": "2019-01-12T00:00:00.000-05",
"drawEndDate": "2019-01-12T00:00:00.000-05"
},
{
"drawTypeDescription": "SPECIAL DRAW",
"drawAttribute": "EVENING",
"drawStartDate": "2019-01-12T00:00:00.000-05",
"drawEndDate": "2019-01-12T00:00:00.000-05"
}
],
"board": [
{
"boardType": "REGULAR",
"selectionMethod": "AUTOPICK",
"selectionSet": [
"2",
"4",
"10",
"12",
"17",
"31"
]
},
{
"boardType": "RAFFLE",
"selectionMethod": "SYSTEMPICK",
"selectionSet": [
"40001722-01"
]
}
]
},
{
"productName": "Encore",
"displayPromoMessage": false,
"drawDetails": [
{
"drawTypeDescription": "REGULAR DRAW",
"drawAttribute": "EVENING",
"drawStartDate": "2019-01-12T00:00:00.000-05",
"drawEndDate": "2019-01-12T00:00:00.000-05"
}
],
"board": [
{
"boardType": "REGULAR",
"selectionMethod": "SYSTEMPICK",
"selectionSet": [
"3440514"
]
}
]
}
]
}
}
const filterCat = _.filter(results, { product: [
{
productName: "Lotto",
board: {
selectionMethod: "AUTOPICK"
}}
]
}
);
console.log(filterCat);
With Pure JS.
You can do this with Javascript's filter function too.
filter function actually works on Arrays, so we use map loop to add objects into arrays first, then we use filter function to get only the data we need!.
let Product = results.buyTicketDetails.product
let getSelectionMethods=(index) => Product[index].board.map((d,i)=>d.selectionMethod)
let getTargetedProducts =(Name,Method)=> Product.map((d,i)=>{
if(Product[i].productName==Name && getselectionMethods(i).indexOf(Method) !==-1){
return d
}
})
let FilteredProducts = getTargetedProducts("Lotto","AUTOPICK").filter((d)=>d !==undefined)
console.log(FilteredProducts)