I have an object which could be nested as deep as possible. I'm trying to determine if object's property ready has at least one false value. If so the checkForFalse function should return false. I got confused while using recursion to solve this problem. What recursion call should return to make this code work? Or I'm completely wrong and missing something?
var obj = {
"currentServiceContractId": {
"ready": true,
"customerPersonId": {
"ready": false
}
},
"siteId": {
"ready": true
},
"districtId": {},
"localityId": {
"ready": true
},
"streetId": {
"ready": true
}
};
function checkForFalse(mainObj) {
let ans = _.find(mainObj || obj, (val) => {
if (_.keys(val).length > 1) {
let readyObj = _.pick(val, 'ready');
return checkForFalse(readyObj);
} else {
return _.get(val, 'ready') === false;
}
});
return _.isEmpty(ans);
}
console.log(checkForFalse(obj));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
This solution uses _.every() recursively to search for ready: false. The _.every() method will return immediately when the callback returns false:
function checkForAllReady(mainObj) {
return _.every(mainObj, (value, key) => {
if(key === 'ready' && value === false) {
return false;
}
if(_.isObject(value)) {
return checkForAllReady(value);
}
return true;
});
}
const obj = {"currentServiceContractId":{"ready":true,"customerPersonId":{"ready":true}},"siteId":{"ready":true},"districtId":{},"localityId":{"ready":true},"streetId":{"ready":true}};
console.log(checkForAllReady(obj));
const objWithFalse = _.merge({}, obj, { "streetId":{"ready":false} })
console.log(checkForAllReady(objWithFalse));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Related
So I have an object called settings with a property called hex, which has its own properties:
var settings = {
hex: {
hex: "4fdaef",
validate: function(){
if(this.hex.length == 6){
return true
}
}
}
}
So currently to get the value of hex I would have to call settings.hex.hex, however ideally I would prefer to be able to call just settings.hex to get the value of the hex. How would I achieve this?
You'll have to rename hex to _hex, but this will work:
var settings = {
get hex() {
return this._hex.hex;
},
_hex: {
hex: "4fdaef",
validate: function () {
if (this.hex.length == 6) {
return true
}
}
}
}
console.log(settings.hex); // 4fdaef
With a Proxy you can allow for settings.hex.validate() to call _settings._hex._hex.validate(), but it's getting real ugly real quick, and we haven't even yet implemented the setter necessary for expected behavior of settings.hex = 'some other color'.
var _settings = {
_hex: {
_hex: new String('4fdaef'),
validate: function () {
if (this.length == 6) {
return true;
}
},
}
}
_settings._hex.hex = new Proxy(_settings._hex._hex, {
get(target, property) {
return property == 'validate' ? _settings._hex.validate : target[property];
}
});
const settings = new Proxy(_settings, {
get(target, property) {
return property == 'hex' ? target._hex.hex : target[property];
}
});
console.log(settings.hex); // [String: '4fdaef']
console.log(settings.hex.validate()); // true
I recently learned about composing objects together using functions from reading this article. Following along, I end up with this code:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
Everything here works. However, I would like to be able to call isFlying as a property instead of a function:
// current (working)
bird.isFlying() // return value of `_isFlying`
// desired
bird.isFlying // return value of `_isFlying`
I know that get and set are keywords that can be used in object literals, and so I tried this:
function withFlying(o) {
let _isFlying = false
return {
...
get isFlying () {
return _isFlying
}
}
}
But it doesn't show the correct value after updating using the other functions. I figured that with the get property being a function, closures would apply similar to the other functions. Am I wrong in this assumption? Is there underlying behavior with get that I'm not understanding, and is what I'm trying to achieve possible the way I'm doing it now?
Here's a snippet with the code I tried to use:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
valueOf_isFlying() {
return _isFlying;
},
get isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
// desired
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // still returns false
console.log(bird.valueOf_isFlying()) // shows _isFlying is true
The problem is that when you create your new object, you're using spread notation to copy the properties from the original object:
return {
...o,
// ...
};
The problem with that is it copies the then-current value of accessor properties, not the definition of the accessor property. You can see that here:
const obj1 = {
get example() {
return 42;
}
};
console.log("Notice that the property descriptor is for an accessor property:");
console.log(Object.getOwnPropertyDescriptor(obj1, "example"));
const obj2 = {...obj1};
console.log("Notice that the property descriptor is for a simple data property:");
console.log(Object.getOwnPropertyDescriptor(obj2, "example"));
.as-console-wrapper {
max-height: 100% !important;
}
It's very much as though you did:
for (const key of Object.keys(o) {
newObject[key] = e[key];
}
e[key] gets the then-current value of the property, not the definition of the property.
To fix it, use Object.getOwnPropertyDesciptors to get the descriptors of the properties, and use Object.defineProperties to define those same properties on the new object. Since you're doing that (and adding more properties) in at least two places, you probably want a utility function:
function assignPropertyDescriptors(target, obj, updates) {
// B
return Object.defineProperties(
// A
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(obj)
),
Object.getOwnPropertyDescriptors(updates)
);
}
The "A" Object.defineProperties call copies the original object's property descriptors and applies them to the new object. The "B" Object.defineProperties call applies the ones you're adding to that new object as well.
But let's generalize that into a loop, similar to Object.assign (hence the name assignPropertyDescriptors):
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
withFlying and withWalking would then use that worker function, for instance:
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
Here's a complete example:
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
function withWalking(o) {
let isWalking = false;
return assignPropertyDescriptors({}, o, {
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
});
}
const bird = withWalking(withFlying({}))
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // _isFlying is true
PC = {a:{ID: "abc",options:{x1:"100", x2:"200"}},b:{ID: "d",options:{x2:"100", x3:"200"}}}
pro = {
"pro": [
{
"pID": "abc",
"attributes": {
"xyz": [
"1",
"2",
"3"
],
"foo": "フルプレミアム"
}
}
]
}
functionX() {
let isND = true;
if (pro === null || pro === [] || pro.length === 0) {
return isND;
} else if (pro.length > 0) {
some(PC, (p) => {
some(p.options, (o, k) => {
some(pro, (item) => {
if (p.ID === item.pID && k === 'xyz') {
if (item.attributes[k] !== []) {
isND = false;
}
} else if (p.ID === item.pID && k !== 'xyz') {
if (item.attributes[k] !== '') {
isND = false;
}
}
});
});
});
}
return isND;
}
I have to iterate through 3 different collections to check my condition and return a value. I am trying to exit the nested some or map if one of my if- else conditions satisfy. I tried passing return true after isND = false but doesn't work. Can someone help resolve this.
Array.prototype.some() will exit early if any of the callbacks return true so you could return the result that way.
It's not very clear but it seems you want to use this "early exit" feature while returning the inverse. How about something like this...
// ignoring "if (pro === null || pro === [] || pro.length === 0)" for this example
// return the inverse
return !Object.values(PC).some(({ ID, options }) => {
return Object.entries(options).some(([k, o]) => {
// here "k" is one of your "x1", "x2", etc keys
// and "o" is the corresponding value
return pro.pro.some(item => {
// return "true" if any of your "conditions" are met
})
})
})
return Object.values(PC).some(({ ID, options }) => {
return Object.entries(options).some(([k]) => {
return (pro.pro).some((item) => {
if (condition) {
if (condition) {
return false;
}
return true;
} else if (condition) {
if (condition) {
return false;
}
return true;
}
return null;
});
});
});
// Haven't returned the inverse of outer function
I have an object like this one:
var BrowserDetect = {
uniqueProps: [],
browserUID: '',
browserFonts: '',
isIPhonePad: function() {
return navigator.userAgent.match(/iPhone|iPod/i);
},
isDesktop: function() {
return !navigator.userAgent.match(/iPhone|iPad|android/i);
},
isAndroid: function() {
return navigator.userAgent.match(/android/i);
},
isFirefox: function() {
return navigator.userAgent.match(/firefox/i);
},
isIOS7: function() {
return navigator.userAgent.match(/.*CPU.*OS 7_\d/i);
},
isChromeCrios: function() {
return navigator.userAgent.match(/chrome|crios/i);
},
isIPad: function() {
return navigator.userAgent.match(/iPad/i);
}
}
(FYI: There are more functions inside the object)
So I want to go through "BrowserDetect" and check which of those functions inside it return "true" and get the function's name too.
What's the easy way to achieve that? I tried to use the jquery $.each, but without success.
Use Object.keys() with Array#filter to iterate the object and return all truthy function names:
Object.keys(BrowserDetect).filter(function(key) {
var f = BrowserDetect[key];
return typeof f === 'function' && f();
});
var BrowserDetect = {
uniqueProps: [],
browserUID: '',
browserFonts: '',
isIPhonePad: function() {
return navigator.userAgent.match(/iPhone|iPod/i);
},
isDesktop: function() {
return !navigator.userAgent.match(/iPhone|iPad|android/i);
},
isAndroid: function() {
return navigator.userAgent.match(/android/i);
},
isFirefox: function() {
return navigator.userAgent.match(/firefox/i);
},
isIOS7: function() {
return navigator.userAgent.match(/.*CPU.*OS 7_\d/i);
},
isChromeCrios: function() {
return navigator.userAgent.match(/chrome|crios/i);
},
isIPad: function() {
return navigator.userAgent.match(/iPad/i);
}
};
var result = Object.keys(BrowserDetect).filter(function(key) {
var f = BrowserDetect[key];
return typeof f === 'function' && f();
});
console.log(result);
I implemented a non-functional solution in pure js. It's fairly straightforward once you consider that a property can be a function as well. Once you use call() method on the property it will run the underlying function.
var BrowserDetect = {
uniqueProps: [],
browserUID: '',
browserFonts: '',
isIPhonePad: function() {
return navigator.userAgent.match(/iPhone|iPod/i);
},
isDesktop: function() {
return !navigator.userAgent.match(/iPhone|iPad|android/i);
},
isAndroid: function() {
return navigator.userAgent.match(/android/i);
},
isFirefox: function() {
return navigator.userAgent.match(/firefox/i);
},
isIOS7: function() {
return navigator.userAgent.match(/.*CPU.*OS 7_\d/i);
},
isChromeCrios: function() {
return navigator.userAgent.match(/chrome|crios/i);
},
isIPad: function() {
return navigator.userAgent.match(/iPad/i);
}
}
var functions = [];
for(var prop in BrowserDetect){
if(typeof(BrowserDetect[prop])=="function" && BrowserDetect[prop].call()){
functions.push(prop);
}
}
console.log(functions);
You can use common for ... in loop
for (var functionName in BrowserDetect) {
if (!BrowserDetect.hasOwnProperty(functionName)
|| typeof BrowserDetect[functionName] !== "function") continue
if (BrowserDetect[functionName]())
return functionName
}
or Object.keys
Object.keys(BrowserDetect).reduce(
(current, fnName) => typeof BrowserDetect[fnName] === "function" && BrowserDetect[fnName]() ? fnName : current )
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
}