Below is running code snippet for the javascript object and array.
I have one jsonObj and here the ResultElementLevel could be the array or
object.
According to I just put if else condition and compare if Array and 'object'.
My question is,How would it be possible without if else condition?
can we write one function which compare object and Array inside single if.
The jsonObj is populating dynamically.
Here it would be possible CHECK object is also come into the Array or Object.
var jsonObj = {
"Response": {
"Errors": {
"Check": {
"_attributes": {
"id": "51416",
"name": "lucyocftest090601"
},
"CheckLevel": {
},
"ResultElementLevel": {
"_text": "Line No (2) [Missing Reporting Category] "
}
}
},
"Success": {
}
}
}
iterateObjorArr(jsonObj);
function iterateObjorArr(jsonObj){
let checkArr = jsonObj.Response.Errors.Check;
let checkID = checkArr._attributes.id;
let checkName = checkArr._attributes.name;
let status = 'failed';
let resultElementLevel = checkArr.ResultElementLevel;
let errorUploadArr = [];
let errorUploadObj;
if (Array.isArray(resultElementLevel)) {
resultElementLevel.map(function (data, index) {
errorUploadObj = {
'id': checkID,
'checkName': checkName,
'status': status,
'errors/warnings': data._text
};
errorUploadArr.push(errorUploadObj);
});
} else {
if (typeof (resultElementLevel) === 'object') {
errorUploadObj = {
'id': checkID,
'checkName': checkName,
'status': status,
'errors/warnings': resultElementLevel._text
};
errorUploadArr.push(errorUploadObj);
}
}
console.log("errorUploadArr", errorUploadArr);
}
You can test to see if resultElementLevel has the length property or not using hasOwnProperty(). Arrays have a length while objects do not (generally):
if (resultElementLevel.hasOwnProperty('length')) {
// Handle it as an array
} else {
// Handle as an object
}
This will, however, only work if the object assigned to resultElementLevel is guaranteed to not have a length property.
My question is,How would it be possible without if else condition? can we write one function which compare object and Array inside single if.
I don't think you'd want to get rid of the condition, but being able to deal with the passed data the same way, wether it's an array, a single item, or null/undefined
You could normalize the data first
function toArray(value){
return value == null? []:
Array.isArray(value)? value:
//isArrayLike(value)? Array.from(value):
[value];
}
//Objects that look like Arrays
function isArrayLike(value){
return value !== null && typeof value === "object" && value.length === (value.length >>> 0);
}
so that from here on, you always deal with an Array:
let errorUploadArr = toArray(checkArr.ResultElementLevel)
.map(function(item){
return {
id: checkID,
checkName: checkName,
status: status,
"errors/warnings": item._text
};
});
var jsonObj = {
Response: {
Errors: {
Check: {
_attributes: {
id: "51416",
name: "lucyocftest090601"
},
CheckLevel: {},
ResultElementLevel: {
_text: "Line No (2) [Missing Reporting Category] "
}
}
},
Success: {}
}
};
iterateObjorArr(jsonObj);
function toArray(value) {
return value == null ? [] :
Array.isArray(value) ? value :
//isArrayLike(value)? Array.from(value):
[value];
}
//Objects that look like Arrays
function isArrayLike(value) {
return value !== null && typeof value === "object" && value.length === (value.length >>> 0);
}
function iterateObjorArr(jsonObj) {
let checkArr = jsonObj.Response.Errors.Check;
let checkID = checkArr._attributes.id;
let checkName = checkArr._attributes.name;
let status = "failed";
let errorUploadArr = toArray(checkArr.ResultElementLevel)
.map(function(data) {
return {
id: checkID,
checkName: checkName,
status: status,
"errors/warnings": data._text
}
});
console.log("errorUploadArr", errorUploadArr);
}
.as-console-wrapper{top:0;max-height:100%!important}
Related
I am trying to create an autocomplete which returns an array of objects using a function. My Object is something like:
this.vehiclesList =
[
{
"additionalDriverContacts": [9929929929, 9992992933, 9873773777],
"id": 1
},
{
"additionalDriverContacts": [8388388388, 8939939999],
"id": 2
}
]
I want to filter the array based on additionalDriverContacts .
My function goes like this:
filterVehicleAdditionalMobile(val: string) {
if (typeof val != 'string') {
return [];
}
let value= val? this.vehiclesList.filter((item) => {
if(item.additionalDriverContacts)
item.additionalDriverContacts.forEach((option)=> {
String(option).toLowerCase().indexOf(val.toLowerCase()) != -1
})
}
}) : this.vehiclesList;
console.log(value)
return value;
}
But in the console value is coming empty array. Where did I go wrong. I tried looking for the solution in this question How do i filter an array inside of a array of objects?
but it didnot help as my usecase is different.
My desired Result should be like:
If 99299 is passed as an argument to the function , then additionalDriverContacts matching that number should be return as an array.
for input 99299, result = [9929929929,9992992933] should be returned
for input 99299, result = [9929929929,9992992933] should be returned
We can use array .map() to extract contacts, then filter down with string .search():
const vehiclesList = [
{"id": 1, "additionalDriverContacts": [9929929929, 9992992933, 9873773777]},
{"id": 2, "additionalDriverContacts": [8388388388, 8939939999]}]
result = getMatchingContacts(vehiclesList, 99299) // run test
console.log(result) // show result
function getMatchingContacts(list, key) {
const arrayOfContacts = list.map(item => item.additionalDriverContacts)
const contacts = [].concat(...arrayOfContacts) // flatten the nested array
.filter(contact => contact.toString().search(key.toString()) >= 0) // find matches
return contacts
}
Hope this helps.
Cheers,
So what you need to do here is first transform each of the items in vehiclesList into an array of matching results, and then concatenate those together.
Give this a try:
var vehiclesList = [{
"additionalDriverContacts": [9929929929, 9992992933, 9873773777],
"id": 1
},
{
"additionalDriverContacts": [8388388388, 8939939999],
"id": 2
}
];
function filterVehicleAdditionalMobile(val) {
if (typeof val != 'string') {
return [];
}
// array of arrays
const values = vehiclesList.map((item) => {
if (!item.additionalDriverContacts) { return []; }
return item.additionalDriverContacts.filter((option) =>
String(option).toLowerCase().indexOf(val.toLowerCase()) != -1
);
});
console.log(values);
// flatten
return Array.prototype.concat.apply([], values);
}
console.log(filterVehicleAdditionalMobile('99'));
Alternatively, you could concatenate all of the items together and then filter them. This is less efficient, but simpler and less code:
var vehiclesList = [{
"additionalDriverContacts": [9929929929, 9992992933, 9873773777],
"id": 1
},
{
"additionalDriverContacts": [8388388388, 8939939999],
"id": 2
}
];
function flatten(values) {
return Array.prototype.concat.apply([], values);
}
function filterVehicleAdditionalMobile(val) {
if (typeof val != 'string') {
return [];
}
return flatten(vehiclesList.map(v => v.additionalDriverContacts || []))
.filter(option => String(option).toLowerCase().indexOf(val.toLowerCase()) != -1);
}
console.log(filterVehicleAdditionalMobile('99'));
Updated : With the last edit of the question
try to change by :
filterVehicleAdditionalMobile(val: string) {
if (typeof val !== 'string') {
return [];
}
let driverContacts = [];
this.vehiclesList.forEach((vehicule) => {
if (vehicule.additionalDriverContacts) {
if (val) {
driverContacts = driverContacts.concat(vehicule.additionalDriverContacts.filter((driverContact) => {
return String(driverContact).toLowerCase().indexOf(val.toLowerCase()) !== -1;
}));
} else {
driverContacts = driverContacts.concat(vehicule.additionalDriverContacts);
}
}
});
return driverContacts;
}
Test :
const driver = this.filterVehicleAdditionalMobile('8');
console.log(driver);
Display :
0: 9873773777
1: 8388388388
2: 8939939999
Below is my request body and which is sending from client side
var credentials = {
"ConsumerData": {
"ConsumerStoreId": "a",
"ConsumerUserId": "a"
},
"CustomerData": {
"CustomerId": "2345678890"
},
"ErnD": {
"UID": "3",
"TxnDt": "1"
},
"PurD": [{
"ItemCode": "3456tghw3",
"ItemEANCode": "223222122"
},
{
"ItemCode": "8jghw3865",
"ItemEANCode": "3334443222"
}
]
}
for testing i am sending var credentials = {} empty credentials
In server side controller(node,express) i want to check req.body empty or not
if(!req.body)
{
console.log('Object missing');
}
if(!req.body.ConsumerData.ConsumerStoreId)
{
console.log('ConsumerStoreId missing');
}
if(!req.body.CustomerData.CustomerId)
{
console.log('CustomerId missing');
}
if(!req.body.ErnD.UID)
{
console.log('UID missing');
}
console.log('outside');
i am checking everything but alwasys its printing outside only
ES2015:
if(req.body.constructor === Object && Object.keys(req.body).length === 0) {
console.log('Object missing');
}
PRE ES2015:
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return JSON.stringify(obj) === JSON.stringify({});
}
if(isEmpty(req.body)) {
console.log('Object missing');
}
For more ways in a pre es2015 style: https://coderwall.com/p/_g3x9q/how-to-check-if-javascript-object-is-empty
if (Object.keys(req.body).length === 0) {
// Do something
}
When the req.body is empty, it returns an empty object, as such, making !req.body return false even when it's empty. Instead, you should test for !Object.keys(req.body).length. What it will do is take every key from the object and count, if no keys where found it would return 0. Then all we need to do is use the same method that we use on empty arrays, testing for !arr.length making it possible to receive a boolean stating it's fullness or the lack of it.
Then, we end up with the code:
router.post('/auth/signup', function(req, res) {
if(!Object.keys(req.body).length) {
// is empty
} else if(!req.body.param1 || !req.body.param2 || !req.body.param3) {
let params = [req.body.param1, req.body.param2, req.body.param3];
let lackingParam = params.findIndex(param => !param === true) > 0 ? params.findIndex(param => !param === true) > 1 ? "req.body.param3" : "req.body.param2" : "req.body.param1";
// lacking only lackingParam
} else {
// not empty
}
});
What about using lodash.isempty
const isEmpty = require('lodash.isempty');
if(isEmpty(req.body)) {
console.log('Empty Object');
}
Here are the docs https://lodash.com/docs/4.17.11#isEmpty
Checks if value is an empty object, collection, map, or set.
Objects are considered empty if they have no own enumerable string
keyed properties.
Array-like values such as arguments objects, arrays, buffers, strings,
or jQuery-like collections are considered empty if they have a length
of 0. Similarly, maps and sets are considered empty if they have a
size of 0.
I have json object which i need to set/override some properties.
This part is done (i too the solution from this answer)
My problem now is handle the case the path to change is not given (or empty.)In this case it shoudl just return an empty Obj which i could then handle and return an error message.
var obj = { "users": {
"profile": {
"name": "markus",
"age": 28
}
}
}
var changes = [
{
path: ['users', 'profile', 'name'],
changes: "Nino"
},
{
path: [],
changes: "fail"
}
];
// Func to set new values
function set(jsonObj, path, value, updatedJson) {
if(path.length === 0 || value.length === 0) {
updatedJson = {};
return updatedJson;
}
if(path.length === 1){
updatedJson[path] = value;
} else {
for(var i = 0; i < path.length-1; i++) {
var elem = path[i];
if( !updatedJson[elem] ) {
updatedJson[elem] = {}
}
updatedJson = updatedJson[elem];
}
updatedJson[path[path.length-1]] = value;
}
return updatedJson;
}
var updatedJson = Object.assign(obj);
changes.forEach( function(changeObj){
var path = changeObj.path;
set(obj, path, changeObj.changes, updatedJson);
});
// handle empty object case
if(Object.keys(updatedJson).length === 0 && obj.constructor === Object){
callback({
success: false,
message: 'File not updated. One or more property are incorrect.'
})
} else {
callback({
success: updatedJson,
message: 'File was succefully updated'
})
}
Changes[0] pass and set new value to the obj.
Changes[1] should instead set updatedJson to empty one, which it does but when i check if Object is empty, updatedJson is full again.
Can someone explain me why is this happening?
And how can i handle error like empty path to object's value?
Try this:
var obj = { "users": {
"profile": {
"name": "markus",
"age": 28
}
}
}
var changes = [
{
path: ['users', 'profile', 'name'],
changes: "Nino"
},
{
path: [],
changes: "fail"
}
];
// Func to set new values
function set(jsonObj, path, value, updatedJson) {
if(path.length === 0 || value.length === 0) {
updatedJson = {};
return updatedJson;
}
if(path.length === 1){
updatedJson[path] = value;
} else {
for(var i = 0; i < path.length-1; i++) {
var elem = path[i];
if( !updatedJson[elem] ) {
updatedJson[elem] = {}
}
updatedJson = updatedJson[elem];
}
updatedJson[path[path.length-1]] = value;
}
return updatedJson;
}
var success = true;
var updatedJson = Object.assign(obj);
changes.forEach( function(changeObj){
var path = changeObj.path;
var result = set(obj, path, changeObj.changes, updatedJson);
if(Object.keys(result).length === 0 && result.constructor === Object)
success = false;
});
// handle empty object case
if(!success){
callback({
success: false,
message: 'File not updated. One or more property are incorrect.'
})
} else {
callback({
success: updatedJson,
message: 'File was succefully updated'
})
}
I have this json object returned from an API that has a few quirks, and I'd like to normalize it so I can process the input the same for every response. These means getting rid of superfluous keys:
Response:
{
_links: {...},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
}
]
}
}
So I'd like to remove all the _embedded keys and flatten it, like so:
{
_links: {...},
foo: [
{
id: 2,
bar: []
}
]
}
This is what I have at the moment, but it only works for the top level and I don't think it'll play well with arrays.
_.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
}
return accumulator[key] = value;
}, {})
Loop in recursion on all of your keys, once you see a key which start with _
simply remove it.
Code:
var
// The keys we want to remove from the Object
KEYS_TO_REMOVE = ['_embedded'],
// The data which we will use
data = {
_links: {'a': 1},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
},
{
id: 3,
_embedded: {
bar: [
{
id: 4,
_embedded: {
bar: []
}
}
]
}
}
]
}
};
/**
* Flatten the given object and remove the desired keys if needed
* #param obj
*/
function flattenObject(obj, flattenObj) {
var key;
// Check to see if we have flatten obj or not
flattenObj = flattenObj || {};
// Loop over all the object keys and process them
for (key in obj) {
// Check that we are running on the object key
if (obj.hasOwnProperty(key)) {
// Check to see if the current key is in the "black" list or not
if (KEYS_TO_REMOVE.indexOf(key) === -1) {
// Process the inner object without this key
flattenObj[key] = flattenObject(obj[key], flattenObj[key]);
} else {
flattenObject(obj[key], flattenObj);
}
}
}
return flattenObj;
}
console.log(flattenObject(data));
So, basically you already have almost all of the code you need. All we have to do is wrap it in a function so we can use recursion. You'll see we only add a check to see if it is an object, if it is, we already have a function that knows how to flatten that object, so we'll just call it again with the key that we need to flatten.
function flatten(temp1) { // Wrap in a function so we can use recursion
return _.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
} else if (value !== null && typeof value === 'object') // Check if it's another object
return _.merge(accumulator, flatten(value)) // Call our function again
return accumulator[key] = value;
}, {})
}
I'll be able to test it in a bit, but this should be what you need.
Got it!
function unEmbed(data) {
return _.reduce(data, function(accumulator, value, key) {
const returnableValue = _.isObject(value) ? unEmbed(value) : value;
if (key === 'embedded') {
return _.merge(accumulator, returnableValue);
}
accumulator[key] = returnableValue;
return accumulator;
}, {});
}
Problem before I was returning return accumulator[key] = returnableValue, which worked out to be return returnableValue.
I am trying to perform a search on an array of vehicles to see if any match the "Make" of "BMW".
Problem: While matches are found and result is given the value true, that value is lost as the function continues the loop. I thought I would be able to break out of the function, anytime a true value is found. The break is not working.
If I cannot break out of the function and must continue looping thru the remainder of that parent node's properties, how can I retain the true value, as once true is found, I am basically done with this node (vehicle).
Thanks
Here is a truncated look at my node tree:
[
{
"title": "2008 BMW 650",
"price": "30,995.00",
"type": "Coupes",
"details" : [{.....}],
"features" : [
{ ..... },
{ "name": "Make", "value": "BMW" },
{ ..... }
]
},
{ ..... }
]
let isPresent = recursiveFilterSearch(node, "Make", "BMW")
function recursiveFilterSearch(node, filterObj, filterValue) {
let result;
for (var key in node) {
// if the any node name & value matches, return true (on this vehicle)
if (node.name !== undefined) {
if (node.name === filterObj && node.value === filterValue) {
result = true;
break; // <-- not doing what I thought it would do
}
}
// if this node property is an array recursively loop thru the array's properties
if (result !== true && Object.prototype.hasOwnProperty.call(node, key)) {
var isArray = Object.prototype.toString.call(node[key]) === '[object Array]';
if (isArray) {
var childrenNode = node[key];
childrenNode.map(function (childNode) {
recursiveFilterSearch(childNode, filterObj, filterValue);
});
}
}
}
return result;
}
Struggled hard on this one, no help from those far smarter than I.
I hope this helps others.
I purposely did not do a search by features (as plalx above suggested), because I want to re-use this code on products that may not have a feature section. One can use this for any product, ie. from cars to shoes to TVs. The property names do not matter.
Make note I purposely lower-cased the respective variables, just to play it safe, as well as using indexOf on the value as my client has such values as "Automatic" & "6-speed Automatic", so index will pick up both when a search is done on "automatic".
collection-filter.js (javascript file)
function recursiveFilterSearch(node, filterObj, filterValue) {
let result = false;
for (const prop in node) {
if (node !== undefined) {
if (node.value !== undefined) {
node.name = (node.name).toLowerCase();
node.value = (node.value).toLowerCase();
if (node.name === filterObj && (node.value).indexOf(filterValue) > -1) {
result = true;
}
}
if (typeof(node[prop]) === 'object') {
recursiveFilterSearch(node[prop], filterObj, filterValue);
}
if (result) {
break;
}
}
}
return result;
}
module.exports = {
filterCollection(coll2Filter, filterName, filterValue) {
const results = [];
coll2Filter.map((node) => {
const isMatch = (recursiveFilterSearch(node, filterName.toLowerCase(), filterValue.toLowerCase()));
if (isMatch) {
results.push(node);
}
});
return results;
}
};
}
Inventory.js: (React.js file using alt flux)
import CollectionFilter from '../../components/forms/helpers/collection-filter.js';
render() {
if (!this.props.items) return <div>Loading ...</div>;
const products = this.props.items;
const result = CollectionFilter.filterCollection(products, 'Trans', 'Automatic');
return (
<div>{ result }</div>
)
.....
You do not assign the return value of your recursive call:
if (result !== true && Object.prototype.hasOwnProperty.call(node, key)) {
var isArray = Object.prototype.toString.call(node[key]) === '[object Array]';
if (isArray) {
var childrenNode = node[key];
childrenNode.map(function (childNode) {
// assign recursive result
result = recursiveFilterSearch(childNode, filterObj, filterValue);
});
}
}
As a side note:
Such a generic search functionality will work but if you are developing new functionality and you have full control over the json structure keep things like 'searchability' in mind.
Were the structure like:
{
features: {
make: "Opel",
ft2: ""
}
}
You could loop all object and search like:
if (car.features.make == "Opel") {
// found in one liner
}