Filter nested object structure - javascript

I have an object with keys representing a schema. A schema contains a definition of columns and I want to end up with a list of columns that match a specific condition. I can use a for each but think it should also be possible to achieve with a filter or reduce; however getting stuck accessing the nested portion and accumulate the results.
I tried a number of variants, with reduce, spread operators but not achieving the desired result.
Hope anyone can pull me out of the quicksand, I tend to sink deeper with every move I make.
<script type="text/javascript">
let data = {
"Schema A" : [{"Id" : "1", "Type" : "measure"}, {"Id" : "2", "Type" : "dimension"}, {"Id" : "3", "Type" : "measure"}],
"Schema B" : [{"Id" : "4", "Type" : "measure"}, {"Id" : "5", "Type" : "dimension"}, {"Id" : "6", "Type" : "measure"}],
};
var d = Object.entries(data); // convert to array
console.log(d);
let result = d.map(val => {
return val.filter(x => x[1].Type === 'measure');
});
console.log(result);
</script>

You need to take the values from the key/value entries for filtering.
const
data = { "Schema A": [{ Id: "1", Type: "measure" }, { Id: "2", Type: "dimension" }, { Id: "3", Type: "measure" }], "Schema B" : [{ Id: "4", Type: "measure" }, { Id: "5", Type: "dimension" }, { Id: "6", Type: "measure" }] },
result = Object
.entries(data)
.map(([k, v]) => [k, v.filter(({ Type }) => Type === 'measure')]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

iterate an array and collect specific data at any match of and additionally provided property value

I have a data structure that looks like this:
var data2 = {
"name" : "history",
"list": [{
"type" : "a",
"max" : 52.346377,
"min" : 0.1354055,
"date": "17-01-01",
"time": "21:38:17"
}, {
"type" : "b",
"max" : 55.3467377,
"min" : 0.1154055,
"date": "17-01-01",
"time": "22:38:17"
}, {
"type" : "b",
"max" : 48.3467377,
"min" : -0.1354055,
"date": "17-01-01",
"time": "23:38:17"
}]
}
I'd like to be able to iterate this data's list property and check if a list item's type value matches another provided value. Then if so, I'd like to collect the max and min values.
How could one achieve this task? I do not know how to figure this one out.
Thanks.
You can use a simple for ... of loop for example;
let checkType = "a";
let max, min;
for (let entry of data2.list) {
if (entry.type === checkType) {
max = entry.max;
min = entry.min;
break;
}
}
console.log(min, max);
Now let's beware this will stop at the first occurence of the right "type" (in case you have multiple entries with the same type);
If you want to account for multiple entries of the same "type", you could combine a filter and map iteration, for example :
let checkType = "b";
let minMaxValues = data2.list
.filter(e => e.type === checkType)
.map(e => { min : e.min, max : e.max });
console.log(minMaxValues);
/*
[{
min : 0.1154055,
max : 55.3467377
},
{
min : -0.1354055,
max : 48.3467377
}] */
"... I wanted all the max and min values for a particular value of type, not the absolute max and absolute min. Any way to include this?"
The probably most natural/obvious approach was to first filter any list item of a provided matching type ...
data2.list.filter(item => item.type === typeToCheckFor)
... and in a second step to map any item of the filtered array ...
.map(item => { min: item.min, max: item.max });
Another approach was to reduce the result within one iteration cycle ...
var data2 = {
"name" : "history",
"list": [{
"type" : "a",
"max" : 52.346377,
"min" : 0.1354055,
"date": "17-01-01",
"time": "21:38:17"
}, {
"type" : "b",
"max" : 55.3467377,
"min" : 0.1154055,
"date": "17-01-01",
"time": "22:38:17"
}, {
"type" : "b",
"max" : 48.3467377,
"min" : -0.1354055,
"date": "17-01-01",
"time": "23:38:17"
}]
}
function collectMinMaxValuesOfMatchingType(collector, item) {
if (item.type === collector.type) {
collector.list.push({
//type: item.type,
min: item.min,
max: item.max
})
}
return collector;
}
console.log(
data2.list.reduce(collectMinMaxValuesOfMatchingType, {
type: 'b',
list: []
}).list
);
console.log(
data2.list.reduce(collectMinMaxValuesOfMatchingType, {
type: 'a',
list: []
}).list
);
console.log(
data2.list.reduce(collectMinMaxValuesOfMatchingType, {
type: 'foo',
list: []
}).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Use filter on data.list will only deliver those objects whose type is identical to the searched value. Afterwards using map to create new objects with the min/max-values.
function filterArray(array, value) {
let result = array.list.filter( obj => obj.type===value).map( filtered => {
return {max: filtered.max, min: filtered.min}
});
return result;
}
var data2 = {
"name" : "history",
"list": [
{
"type" : "a",
"max" : 52.346377,
"min" : 0.1354055,
"date": "17-01-01",
"time": "21:38:17"
},
{
"type" : "b",
"max" : 55.3467377,
"min" : 0.1154055,
"date": "17-01-01",
"time": "22:38:17"
},
{
"type" : "b",
"max" : 48.3467377,
"min" : -0.1354055,
"date": "17-01-01",
"time": "23:38:17"
}
]
}
console.log(filterArray(data2,'b'));
console.log(filterArray(data2,'a'));
console.log(filterArray(data2,'any'));

Looping Through Nested JSON with Nested Child Searches

I have a JSON structure that looks like this:
"benefitValues" : [ {
"changeDate" : "2017-10-13T20:26:13.000+0000",
"changeUserName" : "aaaa",
"numericValue" : 20,
"value" : "20",
"amountType" : {
"allowCustomDataFlg" : false,
"dataType" : "Percent",
"defaultTypeFlg" : true,
"defaultValue" : "Unlimited",
"description" : null,
"maxValue" : null,
"minValue" : null,
"name" : "LIST",
"benefit" : {
"category" : "Facility Services",
"name" : "Single Limit",
"networkStatus" : "IN_NETWORK",
"planType" : "MedicalPlan",
"sortOrder" : 20,
"subcategory" : "Acupuncture Treatment",
"subcategorySortOrder" : 6
}
}
}]
Based on the string "Acupuncture Treatment", I need to extract the the value and the datatype. The dataset is very large, with hundreds of subcategories. I cannot find a good way to search through this data. I tried json-path and advanced-json-path, but if I do a search on a child element, there is no way for me to return the parents. I want my output to look like this:
{
"Subcategory" : "Acupuncture Treatment",
"Value" : "20",
"Type" : "Percent"
}
I was hoping there was an easy way to do this with an existing library, or at least with a simple loop.
This will find the matching element frombenefitValues, and transform the element into the format you're expecting:
var benefitValues = [{
"changeDate": "2017-10-13T20:26:13.000+0000",
"changeUserName": "aaaa",
"numericValue": 20,
"value": "20",
"amountType": {
"allowCustomDataFlg": false,
"dataType": "Percent",
"defaultTypeFlg": true,
"defaultValue": "Unlimited",
"description": null,
"maxValue": null,
"minValue": null,
"name": "LIST",
"benefit": {
"category": "Facility Services",
"name": "Single Limit",
"networkStatus": "IN_NETWORK",
"planType": "MedicalPlan",
"sortOrder": 20,
"subcategory": "Acupuncture Treatment",
"subcategorySortOrder": 6
}
}
}];
// Find the element
let treatment = benefitValues.find((item) => item.amountType.benefit.subcategory === 'Acupuncture Treatment');
let result = {
Value: treatment.value,
Subcategory: treatment.amountType.benefit.subcategory,
Type: treatment.amountType.dataType
}
console.log(result);
You can search through your data set and pull out only the items that match your string by using .filter. That would give you the entire object, so then you can use .map to transform it to the structure you want.
Or if you're only interested in the first result, you can use .find instead.
const json = {"benefitValues" : [{
"changeDate" : "2017-10-13T20:26:13.000+0000",
"changeUserName" : "aaaa",
"numericValue" : 20,
"value" : "20",
"amountType" : {
"allowCustomDataFlg" : false,
"dataType" : "Percent",
"defaultTypeFlg" : true,
"defaultValue" : "Unlimited",
"description" : null,
"maxValue" : null,
"minValue" : null,
"name" : "LIST",
"benefit" : {
"category" : "Facility Services",
"name" : "Single Limit",
"networkStatus" : "IN_NETWORK",
"planType" : "MedicalPlan",
"sortOrder" : 20,
"subcategory" : "Acupuncture Treatment",
"subcategorySortOrder" : 6
}
}
}]};
// With filter/map
const result = json.benefitValues
.filter(val => val.amountType.benefit.subcategory === "Acupuncture Treatment")
.map(val => ({Subcategory: val.amountType.benefit.subcategory, Value: val.value, Type: val.amountType.dataType}));
console.log(result)
// With find / manual transform:
const singleFullResult = json.benefitValues
.find(val => val.amountType.benefit.subcategory === "Acupuncture Treatment")
const singleResult = {
Subcategory: singleFullResult.amountType.benefit.subcategory,
Value: singleFullResult.value,
Type: singleFullResult.amountType.dataType
}
console.log(singleResult)
You can use Array.prototype.filter() combined with Array.prototype.map() and create an array of object with the structure you need. Here's an example:
let myArray = [{
"changeDate": "2017-10-13T20:26:13.000+0000",
"changeUserName": "aaaa",
"numericValue": 20,
"value": "20",
"amountType": {
"allowCustomDataFlg": false,
"dataType": "Percent",
"defaultTypeFlg": true,
"defaultValue": "Unlimited",
"description": null,
"maxValue": null,
"minValue": null,
"name": "LIST",
"benefit": {
"category": "Facility Services",
"name": "Single Limit",
"networkStatus": "IN_NETWORK",
"planType": "MedicalPlan",
"sortOrder": 20,
"subcategory": "Acupuncture Treatment",
"subcategorySortOrder": 6
}
}
}];
let ret = myArray
.filter(arr => arr.amountType.benefit.subcategory === 'Acupuncture Treatment')
.map(arr => {
return {
Subcategory: arr.amountType.benefit.subcategory,
Value: arr.value,
Type: arr.amountType.dataType
};
});
console.log(ret);
First the filter function will filter your array and return only the items related to 'Acupuncture Treatment', then the map function, that receives as a parameter a function that will be executed for each item inside the array and it will return a new structure, will return only the fields you need.

Create new array based of two different arrays (match values)

I have two arrays in JavaScript and I want to re-arrange one based on the order of the first.
First array:
var arrayOne = [
"5",
"1",
"2",
"4"
]
Second array that I want to re-arrange based on page_id that matches to arrayOne:
var arrayTwo = [
{
"page_id" : "5",
"page_title" : "About"
},
{
"page_id" : "2",
"page_title" : "Home"
},
{
"page_id" : "4",
"page_title" : "Contact Us"
},
{
"page_id" : "1",
"page_title" : "Gallery"
},
];
After re-arranged
I want to re-arrange arrayTwo based on page_id, so once re-arranged the second array will look this like:
var arrayTwo = [
{
"page_id" : "5",
"page_title" : "About"
},
{
"page_id" : "1",
"page_title" : "Gallery"
},
{
"page_id" : "2",
"page_title" : "Home"
},
{
"page_id" : "4",
"page_title" : "Contact Us"
},
];
I'm pretty stuck at this point, I've found many sorting 'solutions' on here and Google but they just don't seem to work. I've tried using a for-loop to do this but I didn't really get anywhere. I wouldn't normally ask for help but I've been stuck for a good 3 hours searching.
The match between the first is matched with page_id in the second so there's a match that I can use but as mentioned I'm pretty stuck.
Any help is greatly appreciated, thanks!
First convert the second array into an object - the key will be the page_id, for example:
var map = arrayTwo.reduce((a, b) => {
a[b.page_id] = b;
return a;
}, {});
Now we can easily map the first array, like:
arrayOne.map(x => map[x]);
Working example: http://jsbin.com/tiyudutasi/edit?js,console
You can use Array.prototype.map() to iterate arrayOne, Array.prototype.filter() to match object at arrayTwo having page_id equal to current arrayOne value
var arrayOne = [
"5",
"1",
"2",
"4"
];
var arrayTwo = [{
"page_id": "5",
"page_title": "About"
}, {
"page_id": "2",
"page_title": "Home"
}, {
"page_id": "4",
"page_title": "Contact Us"
}, {
"page_id": "1",
"page_title": "Gallery"
}];
arrayTwo = arrayOne.map(function(id) {
return arrayTwo.filter(function(obj) {
return obj.page_id === id
})[0]
});
console.log(arrayTwo);
You first create an "index" that given an object number can return the number:
var index = {};
arrayTwo.forEach(function(obj){index[obj.page_id] = obj;});
then you can use the index to reorder the objects:
var reordered = arrayOne.map(function(id){return arrayTwo[id];});
You can also use 2 forEach (one per array)
var arrayOne = [
"5",
"1",
"2",
"4"
]
var arrayTwo = [
{
"page_id" : "5",
"page_title" : "About"
},
{
"page_id" : "2",
"page_title" : "Home"
},
{
"page_id" : "4",
"page_title" : "Contact Us"
},
{
"page_id" : "1",
"page_title" : "Gallery"
},
];
var result = [];
arrayOne.forEach(item => arrayTwo.forEach(element => { if (item === element.page_id) result.push(element)}));
console.log(result);
I think using find will be more readable.
arrayOne.map(id => arrayTwo.find(row => row.page_id === id));
If you need to support more browsers, you can use sort instead.
arrayTwo.sort((a, b) => arrayOne.indexOf(a.page_id) - arrayOne.indexOf(b.page_id));

Sort a part of array from another array

I need to sort an array based on another array. However, there is a larger array but I only want to sort on some items properties. The first array looks like this :
var data = [ { id : "23", name : "Item 1", isActive : true},
{ id : "25", name : "Item 2", isActive : false},
{ id : "26", name : "Item 3", isActive : false},
{ id : "30", name : "Item 4", isActive : true},
{ id : "45", name : "Item 5", isActive : true}
]
Then I do this to get the id of active items :
var ids = _.filter(c, function(el) {
return el.isActive === true;
}).map(function(e) { return e.id;}) //["23","30","45"]
I do some operation (database comparaison) and I get for example a result like this :
var sorted = ["45","23","30"]
As I first sorted items by isActive, I only want to sort items that are active. (depending on the sorted array above ) Like this :
var expected = [
{ id : "45", name : "Item 5", isActive : true},
{ id : "23", name : "Item 1", isActive : true},
{ id : "30", name : "Item 4", isActive : true},
//try to keep order for the last values
{ id : "25", name : "Item 2", isActive : false},
{ id : "26", name : "Item 3", isActive : false}]
I tried to use _.sortBy method but this didn't worked as expected :
var sortObj = sorted.reduce(function(acc, value, index) {
acc[value] = value;
return acc;
}, {});
sortedItems = _.sortBy(data, function(x) {
return _.indexOf(sortObj, x.id)
});
But it didn't work as expected
Any ideas ? Huge thanks in advance =)
As you want to keep the order for sorted, and inactive,.
We can use two associated array's to keep an index of the order, you can then use the two associated arrays to create a sort of composite index.
Updated: To hopefully simplify and make easer to follow.
var data = [ { id : "23", name : "Item 1", isActive : true},
{ id : "25", name : "Item 2", isActive : false},
{ id : "26", name : "Item 3", isActive : false},
{ id : "30", name : "Item 4", isActive : true},
{ id : "45", name : "Item 5", isActive : true}
];
var sorted = ["45","23","30"];
//true = active sort, false = data sort
var order = { true: {}, false: {}};
//create order index for data
data.forEach(function (o, i) { order[false][o.id] = i; });
//create order index for sort
sorted.forEach(function (v, i) { order[true][v] = i });
data.sort(function (a, b) {
return (b.isActive - a.isActive) ||
order[a.isActive][a.id] - order[b.isActive][b.id];
});
console.log(data);
You can run 2 filters and combine the result into one array.
return el.isActive === true; and return el.isActive === false;
You can sort both results before inserting into final array
You could use sorting with map and an object for the sort order.
var data = [{ id: "23", name: "Item 1", isActive: true }, { id: "25", name: "Item 2", isActive: false }, { id: "26", name: "Item 3", isActive: false }, { id: "30", name: "Item 4", isActive: true }, { id: "45", name: "Item 5", isActive: true }],
sorted = ["45", "23", "30"],
order = {};
sorted.forEach(function (a, i) { order[a] = i + 1; });
// temporary array holds objects with position and sort-value
var mapped = data.map(function (el, i) {
return { index: i, active: el.isActive, value: order[el.id] || i };
});
// sorting the mapped array containing the reduced values
mapped.sort(function (a, b) {
return b.active - a.active || a.value - b.value;
});
// container for the resulting order
var result = mapped.map(function (el) {
return data[el.index];
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Dynamic Mapping using jquery.map()

I have a series of ajax json responses that I need to convert to a common type of array. The issue is that each json response is slightly different.
Here are two examples of responses I might receive:
var contacts = [{ "ContactId" : "1", "ContactName" : "Bob" },{ "ContactId" : "2", "ContactName" : "Ted" }];
var sites = [{ "SiteId" : "1", "Location" : "MN" },{ "SiteId" : "2", "Location" : "FL" }];
I'm trying to write a method that converts either collection to a common type. An example of the above responses converted would look like this:
var convertedContacts = [{ "value" : "1", "text" : "Bob" },{ "value" : "2", "text" : "Ted" }];
var convertedSites = [{ "value" : "1", "text" : "MN" },{ "value" : "2", "text" : "FL" }];
So I'm trying to use the map function of jquery to facilitate this requirement. Although I can't seem to figure out how to dynamically query for the different property values that will exist depending on which json collection I'm passing into the function.
Here is an example of what I'm trying to do:
function ConvertResponse(arrayToConvert, text, value)
{
var a = $.map(arrayToConvert, function(m) {
return "{ \"text\" : "+eval("m."+text)+", \"value\" : "+eval("m."+value)+" }"
});
}
I also tried this:
function ConvertResponse(arrayToConvert, text, value)
{
var a = $.map(arrayToConvert, function(m) {
return "{ \"text\" : " + m.$(text) + ", \"value\" : " + m.$(value) + " }";
});
}
And this is how you would call it:
var convertedContacts = ConvertResponse(contacts, "ContactName", "ContactId");
var convertedSites = ConvertResponse(contacts, "Location", "SiteId");
Unfortunately this does not seem to work in the least.
Like this?
var contacts = [{ "ContactId" : "1", "ContactName" : "Bob" },{ "ContactId" : "2", "ContactName" : "Ted" }];
var sites = [{ "SiteId" : "1", "Location" : "MN" },{ "SiteId" : "2", "Location" : "FL" }];
function convertResponse(response, text, value) {
return $.map(response, function(it) {
return {
value: it[value],
text: it[text]
};
});
}
var convertedContacts = convertResponse(contacts, 'ContactId', 'ContactName');
var convertedSites = convertResponse(sites, 'SiteId', 'Location');

Categories

Resources