Mapping through array and grouping where property is the same - javascript

I have an array returning like so:
errors = [
{
"row": 1,
"key": "volume",
"errorType": "Data type",
"expectedType": "number",
"receivedType": "string"
},
{
"row": 1,
"key": "units",
"errorType": "Required data",
"expectedType": "string"
},
{
"row": 3,
"key": "year",
"errorType": "Incorrect data type",
"expectedType": "number",
"receivedType": "string"
},
{
"row": 3,
"key": "make",
"errorType": "Required data",
"expectedType": "string"
}
]
I would like to return an array of objects as below:
const errorGrouped = [
{
row:1,
data:[
{
"key":"volume",
"errorType": "Data type",
"expectedType": "number",
"receivedType": "string"
},
{
"key": "units",
"errorType": "Required data",
"expectedType": "string"
}
]
},
{
row:3,
data:[
{
"key": "year",
"errorType": "Incorrect data type",
"expectedType": "number",
"receivedType": "string"
},
{
"key": "make",
"errorType": "Required data",
"expectedType": "string"
}
]
}
]
I have had a go mapping through each object, destructuring into my required form of [{row:.., data:[...]}] but then I can't see an obvious and clean way to group - which suggests to me there's a better way to do this.
But yes, appreciate any help people can provide. Thanks

You can achieve this in a single loop by using hash (JS Object)
Hash will store the row index in the array so that you need not to look up again
once you have the index then you just need to access that array position to push the data
const errors = [
{
"row": 1,
"key": "volume",
"errorType": "Data type",
"expectedType": "number",
"receivedType": "string"
},
{
"row": 1,
"key": "units",
"errorType": "Required data",
"expectedType": "string"
},
{
"row": 3,
"key": "year",
"errorType": "Incorrect data type",
"expectedType": "number",
"receivedType": "string"
},
{
"row": 3,
"key": "make",
"errorType": "Required data",
"expectedType": "string"
}
];
const rowMapIndex = {};
const output = [];
for (let rowObj of errors) {
const { row: rowID, ...restObj } = rowObj;
let addAt = output.length;
if (rowID in rowMapIndex) {
addAt = rowMapIndex[rowID];
} else {
rowMapIndex[rowID] = output.length;
output.push({ row: rowID, data: [] });
}
output[addAt].data.push(restObj)
};
console.log(output);

You can use reduce method in JS.
const errorGrouped = errors.reduce((accumulatorList, error) => {
const group = accumulatorList.find(g => g.row === error.row);
if (group) {
// if the row is already there
group.data.push(error);
} else {
// if this is a new row
accumulatorList.push({ row: error.row, data: [error] });
}
// remove row attribute from the error object
delete error.row;
return accumulatorList;
}, []);
console.log(errorGrouped);

Related

Object to array bug

So I wrote this function to convert an object to an array.
Function:
function objectToArray(obj) {
const result = [];
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object' && value !== null) {
result[key] = objectToArray(value);
} else {
result[key] = value;
}
}
return result;
}
Object that I try to convert:
const obj = {
"1129931367": {
"id": 10,
"amount": 1,
"assets": {
"appid": 252490,
"app_name": "name",
"classid": 1129931367,
"icon_url": "url",
"tradable": 1,
"name": "name",
"market_name": "market name",
"market_hash_name": "market hash name",
"sell_listings": 3215,
"sell_price": "0.10",
"updated_at": "17-Dec-2022"
},
"market_tradable_restriction": 7,
"market_marketable_restriction": 7,
"tags": [
{
"category": "category",
"internal_name": "internal name",
"localized_category_name": "localized category name",
"localized_tag_name": "localized tag name"
},
{
"category": "category",
"internal_name": "internal name",
"localized_category_name": "localized category name",
"localized_tag_name": "localized tag name"
}
]
}
}
Output:
(1129931368) [empty × 1129931367, Array(0)]
But when I try to convert the object that I want to convert it adds a lot of empty arrays and I don't know why. Is this because there is something wrong with the Object or with my function?
Thanks for the help :D
I have tried rewriting the function I provided multiple times but this is as close as I got to what I want.
This is because you are not using the push functionality.
result[key] = value; is essentially pushing to the array in that position.
Instead you need:
function objectToArray(obj) {
const result = [];
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object' && value !== null) {
// push a spread of the array, this avoids nesting arrays
result.push(objectToArray(value));
} else {
result.push(value);
}
}
return result;
}
const initial = {
"1129931367": {
"id": 10,
"amount": 1,
"assets": {
"appid": 252490,
"app_name": "name",
"classid": 1129931367,
"icon_url": "url",
"tradable": 1,
"name": "name",
"market_name": "market name",
"market_hash_name": "market hash name",
"sell_listings": 3215,
"sell_price": "0.10",
"updated_at": "17-Dec-2022"
},
"market_tradable_restriction": 7,
"market_marketable_restriction": 7,
"tags": [
{
"category": "category",
"internal_name": "internal name",
"localized_category_name": "localized category name",
"localized_tag_name": "localized tag name"
},
{
"category": "category",
"internal_name": "internal name",
"localized_category_name": "localized category name",
"localized_tag_name": "localized tag name"
}
]
}
}
console.log(objectToArray(initial))
EDIT: Removed the spread operator to add depth
You could take only the values and take flatMap for a flat result.
function objectToArray(object) {
return Object
.values(object)
.flatMap(value => value && typeof value === 'object'
? objectToArray(value)
: value
);
}
const obj = { "1129931367": { id: 10, amount: 1, assets: { appid: 252490, app_name: "name", classid: 1129931367, icon_url: "url", tradable: 1, name: "name", market_name: "market name", market_hash_name: "market hash name", sell_listings: 3215, sell_price: "0.10", updated_at: "17-Dec-2022" }, market_tradable_restriction: 7, market_marketable_restriction: 7, tags: [{ category: "category", internal_name: "internal name", localized_category_name: "localized category name", localized_tag_name: "localized tag name" }, { category: "category", internal_name: "internal name", localized_category_name: "localized category name", localized_tag_name: "localized tag name" }] } };
console.log(objectToArray(obj));
.as-console-wrapper { max-height: 100% !important; top: 0; }

With identical field item converting into array of value

I am trying to map an array with key like below
const data = [{
"_id": "5f0ffb96d67d70c1a3b143e7",
"name": "USER",
"type": "CUSTOM",
"origin": "USER",
"conditions": [{
"_id": "5f0ffb96d67d70c1a3b143e8",
"field": "status",
"value": "Nomita",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143e9",
"field": "current_status",
"value": "ACTIVE",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143ea",
"field": "user_group_uuid",
"value": "d12s0a7c-11ac-7abf-bl03-f0b70e26f8f2",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143eb",
"field": "user_group_uuid",
"value": "20348751-dcaa-4227-a0ff-912b27180aee",
"operator": "equal"
}]
}]
The above is the input.
const filters_data = { ...data[0] }
const filters_mapping = (array, keyField) =>
array.reduce((obj, item) => {
obj[item[keyField]] = item
return obj
}, {})
const filter_items = filters_mapping(filters_data.conditions,'field');
From this code I am getting
{ user_status:
{ _id: 5f0ffb96d67d70c1a3b143e8,
field: 'status',
value: 'Nomita',
operator: 'equal' },
current_status:
{ _id: 5f0ffb96d67d70c1a3b143e9,
field: 'current_status',
value: 'ACTIVE',
operator: 'equal' },
user_group_uuid:
{ _id: 5f0ffb96d67d70c1a3b143eb,
field: 'user_group_uuid',
value: 'd12s0a7c-11ac-7abf-bl03-f0b70e26f8f2',
operator: 'equal' } }
But I actually want something like this, that maps the value in array when the field is similar.
{ user_status:
{ _id: 5f0ffb96d67d70c1a3b143e8,
field: 'status',
value: 'Nomita',
operator: 'equal' },
current_status:
{ _id: 5f0ffb96d67d70c1a3b143e9,
field: 'current_status',
value: 'ACTIVE',
operator: 'equal' },
user_group_uuid:
{ _id: 5f0ffb96d67d70c1a3b143eb,
field: 'user_group_uuid',
value: ['d12s0a7c-11ac-7abf-bl03-f0b70e26f8f2','20348751-dcaa-4227-a0ff-912b27180aee'],
operator: 'equal' } }
this should happen only when there's multiple fields are available or else, it would return normal string only.
Ciao, I'm using your code but before reduce I combine value with same field value.
Here working example:
const data = [{
"_id": "5f0ffb96d67d70c1a3b143e7",
"name": "USER",
"type": "CUSTOM",
"origin": "USER",
"conditions": [{
"_id": "5f0ffb96d67d70c1a3b143e8",
"field": "status",
"value": "Nomita",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143e9",
"field": "current_status",
"value": "ACTIVE",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143ea",
"field": "user_group_uuid",
"value": "d12s0a7c-11ac-7abf-bl03-f0b70e26f8f2",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143eb",
"field": "user_group_uuid",
"value": "20348751-dcaa-4227-a0ff-912b27180aee",
"operator": "equal"
}]
}]
const conditions_combined = _.uniqWith(data[0].conditions, function(a, b) {
if (a.field === b.field) {
b.value += ', ' + a.value;
b.value = b.value.split(",");
return true;
}
});
data[0].conditions = conditions_combined;
const filters_data = { ...data[0] };
const filters_mapping = (array, keyField) =>
array.reduce((obj, item) => {
obj[item[keyField]] = item
return obj
}, {})
const filter_items = filters_mapping(filters_data.conditions,'field');
console.log(filter_items);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.10/lodash.min.js"></script>
Explanation: I'm using _.uniqWith from lodash to combine value with same field value.
Here is one possible soluce.
Your code doesn't handle the case where the key already exists.
const data = [{
"_id": "5f0ffb96d67d70c1a3b143e7",
"name": "USER",
"type": "CUSTOM",
"origin": "USER",
"conditions": [{
"_id": "5f0ffb96d67d70c1a3b143e8",
"field": "status",
"value": "Nomita",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143e9",
"field": "current_status",
"value": "ACTIVE",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143ea",
"field": "user_group_uuid",
"value": "d12s0a7c-11ac-7abf-bl03-f0b70e26f8f2",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143eb",
"field": "user_group_uuid",
"value": "20348751-dcaa-4227-a0ff-912b27180aee",
"operator": "equal"
}]
}];
const mutatedData = data[0].conditions.reduce((tmp, {
_id,
field,
value,
operator,
}) => {
// Handle the case where the field already exists
if (tmp[field]) {
tmp[field].value = [
...(tmp[field].value instanceof Array ?
tmp[field].value : [tmp[field].value]),
value,
];
return tmp;
}
// Handle the case where the field doesn't exist yet
tmp[field] = {
_id,
field,
value,
operator,
};
return tmp;
}, {});
console.log(mutatedData);
Condensed version
const data = [{
"_id": "5f0ffb96d67d70c1a3b143e7",
"name": "USER",
"type": "CUSTOM",
"origin": "USER",
"conditions": [{
"_id": "5f0ffb96d67d70c1a3b143e8",
"field": "status",
"value": "Nomita",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143e9",
"field": "current_status",
"value": "ACTIVE",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143ea",
"field": "user_group_uuid",
"value": "d12s0a7c-11ac-7abf-bl03-f0b70e26f8f2",
"operator": "equal"
}, {
"_id": "5f0ffb96d67d70c1a3b143eb",
"field": "user_group_uuid",
"value": "20348751-dcaa-4227-a0ff-912b27180aee",
"operator": "equal"
}]
}];
const mutatedData = data[0].conditions.reduce((tmp, {
_id,
field,
value,
operator,
}) => (tmp[field] = tmp[field] ? ((tmp[field].value = [
...(tmp[field].value instanceof Array ?
tmp[field].value : [tmp[field].value]),
value,
]) && tmp[field]) : {
_id,
field,
value,
operator,
}) && tmp, {});
console.log(mutatedData);

How to map key values in sub sub objects from an array in java script

I was trying to access key, values from the below array
I am trying to access keys inside the fields object and value from the model inside keys object
formFields = [
{
"title": "Criteria Details",
"columns": 2,
"fields": {
"criteriaName": {
"type": "text",
"label": "Criteria Name",
"id": 'criteriaName',
"model": "CRITERIA 1",
"required": true,
"show": true,
"rules": [
v => !!v || 'Criteria Name is required',
]
},
"criteriaType": {
"type": "select",
"label": "Criteria type",
"id": "criteriaType",
"options": ['Dependent', 'Independent', 'Static'],
"model": "Dependent",
"required": true,
"rules": [
v => !!v || 'Criteria Type is required',
],
"show": true,
},
"table": {
"type": "select",
"label": "Table",
"id": "table",
"options": ["Table1"],
"model": "Table1",
"required": true,
"rules": [
v => !!v || 'Table is required',
],
"show": true,
},
"column": {
"type": "select",
"label": "Column",
"id": "column",
"options": ["Column1"],
"model": "Column1",
"required": true,
"rules": [
v => !!v || 'Column is required',
],
"show": true,
},
"joinType": {
"type": "select",
"label": "Join Type",
"id": "joinType",
"options": ["AND", "OR"],
"model": "OR",
"required": true,
"rules": [
v => !!v || 'Join Type is required',
],
"show": true,
},
"operator": {
"type": "select",
"label": "Operator",
"id": "operator",
"options": ["<", "<=", "<>", "=", ">=", ">", "EXISTS", "IN", "IS NOT NULL", "NULL", "LIKE", "NOT EXISTS", "NOT IN", "NOT LIKE"],
"model": ">=",
"required": true,
"rules": [
v => !!v || 'Operator is required',
],
"show": true,
},
"valueType": {
"type": "select",
"label": "Value Type",
"id": "valueType",
"options": ["Dependent SQL", "SQL", "VALUE"],
"model": "SQL",
"required": true,
"rules": [
v => !!v || 'Value Type is required',
],
"show": true,
},
"dataType": {
"type": "select",
"label": "Data Type",
"id": "dataType",
"options": ["DATE", "NUMBER", "STRING"],
"model": "NUMBER",
"required": true,
"rules": [
v => !!v || 'Data Type is required',
],
"show": true,
},
"format": {
"type": "text",
"label": "Format",
"id": "format",
"model": "Config",
"required": false,
"show": true,
},
"parameterMandatory": {
"type": "select",
"label": "Parameter Mandatory",
"id": "parameterMandatory",
"options": ["NO", "YES"],
"model": "YES",
"required": true,
"rules": [
v => !!v || 'Parameter Mandatory is required',
],
"show": true,
},
"link": {
"type": "select",
"label": "Link",
"id": "link",
"options": ["KB"],
"model": "KB",
"required": false,
"show": true,
},
"sequence": {
'type': "text",
"label": "Sequence",
"id": "sequence",
"model": "SEQ1",
"required": true,
"rules": [
v => !!v || 'Sequence is required',
],
"show": true,
},
"value": {
"type": "description_notes",
"label": "Value",
"id": "value",
"model": "VAL",
"required": true,
"rules": [
v => !!v || 'Value is required',
],
"show": true,
}
}
},
{
'title': "Notes",
"columns": 1,
"fields": {
"description": {
"type": "description_notes",
"label": "Description",
"id": "description",
"required": false,
"model": 'abcde',
"show": true,
}
}
}
]
****The Output i was trying is like this.****
How to access the keys and values from the above array like this.
Which method we need to use
criteriaDetails: [
{"criteriaName": "CRITERIA 1"},
{"criteriaType": "Dependent"},
{"table": "Table1"},
{"column": "Column1"},
{"joinType": "OR"},
{"operator": ">="},
{"valueType": "SQL"},
{"dataType": "NUMBER"},
{"format": "Config"},
{"parameterMandatory": "YES"},
{"link": "KB"},
{"sequence": "SEQ1"},
{"value": "VAL"},
{"description": "abcde"}
]
I tried below code
const field = this.formFields.map(field => {
return Object.entries(field.fields)
})
console.log(field)
how can I achieve this?
How to solve this. Please help
Thanks..
It looks like all you need to do is iterate over each fields object, and extract the key and the model property from each:
const formFields=[{"title":"Criteria Details","columns":2,"fields":{"criteriaName":{"type":"text","label":"Criteria Name","id":'criteriaName',"model":"CRITERIA 1","required":!0,"show":!0,"rules":[v=>!!v||'Criteria Name is required',]},"criteriaType":{"type":"select","label":"Criteria type","id":"criteriaType","options":['Dependent','Independent','Static'],"model":"Dependent","required":!0,"rules":[v=>!!v||'Criteria Type is required',],"show":!0,},"table":{"type":"select","label":"Table","id":"table","options":["Table1"],"model":"Table1","required":!0,"rules":[v=>!!v||'Table is required',],"show":!0,},"column":{"type":"select","label":"Column","id":"column","options":["Column1"],"model":"Column1","required":!0,"rules":[v=>!!v||'Column is required',],"show":!0,},"joinType":{"type":"select","label":"Join Type","id":"joinType","options":["AND","OR"],"model":"OR","required":!0,"rules":[v=>!!v||'Join Type is required',],"show":!0,},"operator":{"type":"select","label":"Operator","id":"operator","options":["<","<=","<>","=",">=",">","EXISTS","IN","IS NOT NULL","NULL","LIKE","NOT EXISTS","NOT IN","NOT LIKE"],"model":">=","required":!0,"rules":[v=>!!v||'Operator is required',],"show":!0,},"valueType":{"type":"select","label":"Value Type","id":"valueType","options":["Dependent SQL","SQL","VALUE"],"model":"SQL","required":!0,"rules":[v=>!!v||'Value Type is required',],"show":!0,},"dataType":{"type":"select","label":"Data Type","id":"dataType","options":["DATE","NUMBER","STRING"],"model":"NUMBER","required":!0,"rules":[v=>!!v||'Data Type is required',],"show":!0,},"format":{"type":"text","label":"Format","id":"format","model":"Config","required":!1,"show":!0,},"parameterMandatory":{"type":"select","label":"Parameter Mandatory","id":"parameterMandatory","options":["NO","YES"],"model":"YES","required":!0,"rules":[v=>!!v||'Parameter Mandatory is required',],"show":!0,},"link":{"type":"select","label":"Link","id":"link","options":["KB"],"model":"KB","required":!1,"show":!0,},"sequence":{'type':"text","label":"Sequence","id":"sequence","model":"SEQ1","required":!0,"rules":[v=>!!v||'Sequence is required',],"show":!0,},"value":{"type":"description_notes","label":"Value","id":"value","model":"VAL","required":!0,"rules":[v=>!!v||'Value is required',],"show":!0,}}},{'title':"Notes","columns":1,"fields":{"description":{"type":"description_notes","label":"Description","id":"description","required":!1,"model":'abcde',"show":!0,}}}]
const output = [];
formFields.forEach(({ fields }) => {
Object.entries(fields).forEach(([key, { model }]) => {
output.push({ [key]: model });
});
});
console.log(output);
The ({ fields}) is destructuring the object you pass to the callback. It would be equivalent to
formFields.forEach((obj) => {
Object.entries(obj.fields)
Object.entries will return an array with two values, the first is the current key and the second is the object associated with the key. That's why you can destructure again with (([key, { model }]).
As a reference you could rewrite the whole thing as
formFields.forEach((curObj) => {
Object.entries(curObj.fields).forEach(([key, fieldValues]) => {
output[key] = fieldValues.model;
});
});
const formFields = [{"title":"Criteria Details","columns":2,"fields":{"criteriaName":{"type":"text","label":"Criteria Name","id":'criteriaName',"model":"CRITERIA 1","required":!0,"show":!0,"rules":[v=>!!v||'Criteria Name is required',]},"criteriaType":{"type":"select","label":"Criteria type","id":"criteriaType","options":['Dependent','Independent','Static'],"model":"Dependent","required":!0,"rules":[v=>!!v||'Criteria Type is required',],"show":!0,},"table":{"type":"select","label":"Table","id":"table","options":["Table1"],"model":"Table1","required":!0,"rules":[v=>!!v||'Table is required',],"show":!0,},"column":{"type":"select","label":"Column","id":"column","options":["Column1"],"model":"Column1","required":!0,"rules":[v=>!!v||'Column is required',],"show":!0,},"joinType":{"type":"select","label":"Join Type","id":"joinType","options":["AND","OR"],"model":"OR","required":!0,"rules":[v=>!!v||'Join Type is required',],"show":!0,},"operator":{"type":"select","label":"Operator","id":"operator","options":["<","<=","<>","=",">=",">","EXISTS","IN","IS NOT NULL","NULL","LIKE","NOT EXISTS","NOT IN","NOT LIKE"],"model":">=","required":!0,"rules":[v=>!!v||'Operator is required',],"show":!0,},"valueType":{"type":"select","label":"Value Type","id":"valueType","options":["Dependent SQL","SQL","VALUE"],"model":"SQL","required":!0,"rules":[v=>!!v||'Value Type is required',],"show":!0,},"dataType":{"type":"select","label":"Data Type","id":"dataType","options":["DATE","NUMBER","STRING"],"model":"NUMBER","required":!0,"rules":[v=>!!v||'Data Type is required',],"show":!0,},"format":{"type":"text","label":"Format","id":"format","model":"Config","required":!1,"show":!0,},"parameterMandatory":{"type":"select","label":"Parameter Mandatory","id":"parameterMandatory","options":["NO","YES"],"model":"YES","required":!0,"rules":[v=>!!v||'Parameter Mandatory is required',],"show":!0,},"link":{"type":"select","label":"Link","id":"link","options":["KB"],"model":"KB","required":!1,"show":!0,},"sequence":{'type':"text","label":"Sequence","id":"sequence","model":"SEQ1","required":!0,"rules":[v=>!!v||'Sequence is required',],"show":!0,},"value":{"type":"description_notes","label":"Value","id":"value","model":"VAL","required":!0,"rules":[v=>!!v||'Value is required',],"show":!0,}}},{'title':"Notes","columns":1,"fields":{"description":{"type":"description_notes","label":"Description","id":"description","required":!1,"model":'abcde',"show":!0,}}}]
let result = []
formFields.forEach(item => {
const entries = Object.entries(item.fields)
entries.forEach(([key, { model }]) => {
result.push({
[key]: model
})
})
})
console.log(result)
There are many ways to do this.
You can use the Array.prototype.reduce() method to reduce the array to a single value, and use the Object.entries static method to extract the key and value
const formFields=[{"title":"Criteria Details","columns":2,"fields":{"criteriaName":{"type":"text","label":"Criteria Name","id":'criteriaName',"model":"CRITERIA 1","required":!0,"show":!0,"rules":[v=>!!v||'Criteria Name is required',]},"criteriaType":{"type":"select","label":"Criteria type","id":"criteriaType","options":['Dependent','Independent','Static'],"model":"Dependent","required":!0,"rules":[v=>!!v||'Criteria Type is required',],"show":!0,},"table":{"type":"select","label":"Table","id":"table","options":["Table1"],"model":"Table1","required":!0,"rules":[v=>!!v||'Table is required',],"show":!0,},"column":{"type":"select","label":"Column","id":"column","options":["Column1"],"model":"Column1","required":!0,"rules":[v=>!!v||'Column is required',],"show":!0,},"joinType":{"type":"select","label":"Join Type","id":"joinType","options":["AND","OR"],"model":"OR","required":!0,"rules":[v=>!!v||'Join Type is required',],"show":!0,},"operator":{"type":"select","label":"Operator","id":"operator","options":["<","<=","<>","=",">=",">","EXISTS","IN","IS NOT NULL","NULL","LIKE","NOT EXISTS","NOT IN","NOT LIKE"],"model":">=","required":!0,"rules":[v=>!!v||'Operator is required',],"show":!0,},"valueType":{"type":"select","label":"Value Type","id":"valueType","options":["Dependent SQL","SQL","VALUE"],"model":"SQL","required":!0,"rules":[v=>!!v||'Value Type is required',],"show":!0,},"dataType":{"type":"select","label":"Data Type","id":"dataType","options":["DATE","NUMBER","STRING"],"model":"NUMBER","required":!0,"rules":[v=>!!v||'Data Type is required',],"show":!0,},"format":{"type":"text","label":"Format","id":"format","model":"Config","required":!1,"show":!0,},"parameterMandatory":{"type":"select","label":"Parameter Mandatory","id":"parameterMandatory","options":["NO","YES"],"model":"YES","required":!0,"rules":[v=>!!v||'Parameter Mandatory is required',],"show":!0,},"link":{"type":"select","label":"Link","id":"link","options":["KB"],"model":"KB","required":!1,"show":!0,},"sequence":{'type':"text","label":"Sequence","id":"sequence","model":"SEQ1","required":!0,"rules":[v=>!!v||'Sequence is required',],"show":!0,},"value":{"type":"description_notes","label":"Value","id":"value","model":"VAL","required":!0,"rules":[v=>!!v||'Value is required',],"show":!0,}}},{'title':"Notes","columns":1,"fields":{"description":{"type":"description_notes","label":"Description","id":"description","required":!1,"model":'abcde',"show":!0,}}}]
const output = formFields.reduce((accumulator, { fields }) => {
return Object.assign(accumulator, Object.entries(fields)
.reduce((accumulatorInside, [key, { model }]) => {
accumulatorInside[key] = model;
return accumulatorInside;
}, {})
)
}, {});
console.log(output);
Or you can use loops
const output = {};
for (let index = 0; index < formFields.length; index++) {
for (let key in formFields[index].fields) {
if (formFields[index].fields.hasOwnProperty(key))
output[key] = formFields[index].fields[key].model;
}
}
console.log(output);
You could also do this with Array.prototype.forEach()(from #certainperformance answer, go up-vote that too)
const output = {};
formFields.forEach(({ fields }) => {
Object.entries(fields).forEach(([key, { model }]) => {
output[key] = model;
});
});
console.log(output);
The basic idea is the same regardless. You need to iterate over the properties and set the values.
From a performance standpoint: closures that move outside their scope are slower than functions that do not, and for...in loops are the fastest, but the most verbose to use.
BE CAREFUL! for...in loops do have some caveats! The reason I do the check for the own property is to prevent copying any prototypes. This is the one issue with the loop. You may instead want to use a for loop with the array returned with Object.getOWnPropertyNames() static method instead since it is safer and won't traverse prototypes, but it adds extra time complexity.
These are however considerations when you don't want to use side effects(Array.prototype.forEach()) or need to squeeze as MUCH speed as possible out of your code.

How to define JSON Schema that requires an array of objects to contain at least 2 properties of a certain value?

I have a dataset that looks like this:
Valid Example Data
{
type: "step",
label: "Step 1",
fields: [
// An optional field
{
type: "email",
label: "Your Email"
},
// A field that is required and can be either amount|preset
{
type: "amount",
label: "Amount"
},
// A field that is required and can be either credit_card|ach
{
type: "credit_card",
label: "Credit Card"
}
]
}
The fields array can contain many objects of various types. The above example would be valid.
Invalid Example Data
{
type: "step",
label: "Step 1",
fields: [
{
type: "email",
label: "Your Email"
},
{
type: "credit_card",
label: "Credit Card"
}
]
}
This should error as it does not include an object of type amount or presets
Validation Rules
In order to be valid, fields needs to contain 2 objects.
1 of them must be of either { type: "amount" } or { type: "presets" }
1 of them must be of either { type: "credit_card" } or { type: "ach" }
Any combination of the 2 would make fields be valid.
JSON Schema
Here is my (failing) JSON Schema:
{
"title": "step",
"type": "object",
"properties": {
"type": {
"title": "type",
"type": "string"
},
"label": {
"title": "label",
"type": "string"
},
"fields": {
"title": "fields",
"description": "Array of fields",
"type": "array",
"additionalItems": true,
"minItems": 1,
"items": {
"type": "object",
"anyOf": [
{ "properties": { "type": { "enum": ["amount", "preset"] } } },
{ "properties": { "type": { "enum": ["credit_card", "ach"] } } }
],
"properties": {
"type": {
"type": "string",
}
}
},
}
},
"required": ["type", "label", "fields"]
}
Here is the JSON Schema Validation Reference
I think between contains, anyOf, allOf, oneOf, and enum I should be able to do this?
Put the following in your /properties/fields schema. That expresses the constraints you need. Remove /properties/fields/items/anyOf (it's wrong) and /properties/fields/additionalItems (it doesn't do anything).
"allOf": [
{
"contains": {
"properties": { "type": { "enum": ["amount", "presets"] } }
}
},
{
"contains": {
"properties": { "type": { "enum": ["credit_card", "ach"] } }
}
}
]

Searching JSON objects with JQuery

I have the following JSON object. Using JQuery I need to find the values of the following:
summary.nameValues.ID and detail.TypedNameValues.size
Could somebody please show how this can be achieved using JQuery?
[
{
"path": "\\Users\\john.smith\\test",
"summary": {
"NameValues": [
{
"Name": "Id",
"Values": [
"232639"
]
},
{
"Name": "City",
"Values": [
"London"
]
}
]
},
"detail": {
"String": "some data",
"Result": 0,
"TypedNameValues": [
{
"Name": "name1",
"Type": "string",
"Value": "data here!!"
},
{
"Name": "size",
"Type": "long",
"Value": "434353"
}
]
}
}
]
jQuery doesn't work on plain object literals. You can use the below function in a similar way to search all 'id's (or any other property), regardless of its depth in the object:
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i == key && obj[key] == val) {
objects.push(obj);
}
}
return objects;
}
Use like so:
getObjects(TestObj, 'id', 'A'); // Returns an array of matching objects
This answer taken from another thread. You may find more help here: use jQuery's find() on JSON object
Performing this kind of queries on JSON structures are trivial using DefiantJS (http://defiantjs.com). This lib extends the global object JSON with the method "search" - with which one can execute XPath expressive searches.
Check out this fiddle;
http://jsfiddle.net/hbi99/kLE2v/
The code can look like this:
var data = [
{
"path": "\\Users\\john.smith\\test",
"summary": {
"NameValues": [
{
"Name": "Id",
"Values": "232639"
},
{
"Name": "City",
"Values": "London"
}
]
},
"detail": {
"String": "some data",
"Result": 0,
"TypedNameValues": [
{
"Name": "name1",
"Type": "string",
"Value": "data here!!"
},
{
"Name": "size",
"Type": "long",
"Value": "434353"
}
]
}
}
],
res = JSON.search( data, '//*[Name="size"]' );
console.log( res[0].Value );
// 434353
Some one else as already answered, either way here is my version for the same.
<textarea id="ta" style="display:none;">[
{
"path": "\\Users\\john.smith\\test",
"summary": {
"NameValues": [
{
"Name": "Id",
"Values": [
"232639"
]
},
{
"Name": "City",
"Values": [
"London"
]
}
]
},
"detail": {
"String": "some data",
"Result": 0,
"TypedNameValues": [
{
"Name": "name1",
"Type": "string",
"Value": "data here!!"
},
{
"Name": "size",
"Type": "long",
"Value": "434353"
}
]
}
}
]</textarea>
Parser
var obj = $.parseJSON($('#ta').val());
var nameValues = obj[0].summary.NameValues;
var typedNameValues = obj[0].detail.TypedNameValues;
function getObjByName(o, name) {
for (var i = 0; i < o.length; i++) {
if (o[i].Name == name) {
return o[i];
}
}
return null;
}
alert(getObjByName(nameValues, 'Id').Values.join(", "));
alert(getObjByName(typedNameValues, 'size').Value);
A working fiddle for you on the same.
http://jsfiddle.net/3EVE4/

Categories

Resources