Can I turn a Object.property into a String like "Object.property"? - javascript

I want to create a function to turn the keys from an object that i am accessing to a string, something like:
const test = {
propertyOne: {
propertyTwo: 0
},
propertyThree: {
propertyFour: 0
}
}
function ObjectKeysToString(objectReceived) {
...
return stringfiedObjectReceived
}
Input:
test.propertyOne.propertyTwo
ObjectKeysToString(test.propertyOne.propertyTwo)
Output: "test.propertyOne.propertyTwo"
Is this possible?
I've searched for this and looked for every Object method but didnt succeeded on any of them

The best you can do is propertyOne.propertyTwo. Variable names can't be accessed in runtime
const test = {
propertyOne: {
propertyTwo: 0
}
}
function ObjectKeysToString(objectReceived) {
const path = []
let current = objectReceived
do {
const key = Object.keys(current)[0]
path.push(key)
current = current[key]
} while (typeof current === 'object' && current !== null)
return path.join('.')
}
console.log(ObjectKeysToString(test))

Related

Adding dynamic properties to object only if the name is defined

i have a function like this:
const getKeysAs = (key1, key2) => {
return {
[key1]: state.key1,
[key2]: state.key2
}
}
So if state.key1 is 'a' and state.key2 is 'b', calling getKyesAs('one', 'two') would return
{
one: 'a',
two: 'b'
}
Now, if one of the argument is undefined, is there a way to not include it in the returned object ?
You can Conditionally add properties to an Object with object destructuring
const obj = {
...(key1 && { [key1]: state[key1] }),
...(key2 && { [key2]: state[key2] })
};
If some of the args function is undefined, null or 0 (falsy values) then it will no be added to the object.
There is a very scalable way to do it:
const state= {
a: "hello",
}
function getKeysAs (keys) {
return [...arguments].reduce((acc, cur)=> {
const newValue = state[cur] && {[cur]: state[cur]}
return {...acc, ...newValue}
}, {})
}
console.log(getKeysAs("a", "b"))
This way, you can pass as much keys as you need without worrying about scalability & undefined values.
Use Object.assign().
const getKeysAs = (key1, key2) => {
return Object.assign({}, key1 && {[key1]: state[key1]}, key2 && {[key2]: state[key2]});
}
Assuming you actually mean to do state[key1], not state.key1, here is a solution that doesn't create superfluous objects:
const getKeysAs = (...keys) => {
const result = {};
for (const key of keys) {
if (key != null) {
result[key] = state[key];
}
}
return result;
}

How to check if two elements are in an object

I am trying to make a function that checks if a key ( a number ) exists in an object, and then if that element has a specific sub element. this is what I am doing
const blacklistedCities = {
[NATION_ID_USA]: [
'New York',
],
[NATION_ID_JAPAN]: [
'Tokio',
'Kyoto',
]
}
export function isBlacklistedCity(city, nationId) {
if(blacklistedCities.indexOf(nationId) !== -1 && blacklistedCities. ??WHAT SHOULD I PUT HERE??) {
return false;
}
return true;
}
NATION_ID_USA and NATION_ID_JAPAN are constants imported from another file
this function should return false when the element is found because I am using it from a filter() function somewhere else but I am open to any suggestion
thanks
I'd think of the input object as describing a much simpler array of scalar values that will be queried.
// Convert the input to a set containing [ 'nationId-cityId', ...]
//
const blacklistedCities = {
NATION_ID_USA: [
'New York',
],
NATION_ID_JAPAN: [
'Tokio',
'Kyoto',
]
}
const keys = Object.entries(blacklistedCities).reduce((acc, [nationId, cityIdArray]) => {
let nationIds = cityIdArray.map(cityId => `${nationId}-${cityId}`)
acc = acc.concat(nationIds)
return acc
}, [])
// keep this set around for queries
const queryMe = new Set(keys)
// and query with nation, key pair
function queryFor(nationId, cityId) {
return queryMe.has(`${nationId}-${cityId}`)
}
console.log(queryFor('NATION_ID_USA', 'New York')) // -> true
console.log(queryFor('NATION_ID_FRANCE', 'Paris')) // -> false
You can use the nation name as a property name to index directly into the object, and then use .includes() on the array (if present):
export function isBlacklistedCity(city, nationId) {
return (nationId in blacklistedCitites) && blacklistedCities[nationId].includes(city);
}
You can do something like this
const blacklistedCities = {
NATION_ID_USA: [
'New York',
],
NATION_ID_JAPAN: [
'Tokio',
'Kyoto',
]
}
function isBlacklistedCity(city, nationId) {
if(blacklistedCities[nationId] && blacklistedCities[nationId].length > 0 && blacklistedCities[nationId].find((cityInObj) => cityInObj==city)) {
return true;
} else {
return false;
}
}
console.log(isBlacklistedCity("Tokio", "NATION_ID_JAPAN"));

Better way to map a deep object to new object

This code works for converting the JSON to an object where each name object turns into the key for either its value, or if it instead has its own element object breaks that out and does the same to its contents.
Is there a better way to do this that would also allow for more extensiblity of the JSON schema?
Is there a way I can get it all down to a simpler function that I can pass the first element and have it convert it down to whatever depth the schema goes?
const fs = require('fs');
{
let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version='1.0'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let depth = 0;
var compiled = {
[scheme.ele.name]: scheme.ele.ele.map(function(i) {
if (typeof i.ele != 'undefined') {
return {
[i.name]: i.ele.map(function(k) {
if (typeof k.ele != 'undefined') {
return {
[k.name]: k.ele.map(function(p) {
if (typeof p.ele != 'undefined') {
return {
[p.name]: p.ele
};
} else {
return {
[p.name]: p.value
};
}
})
};
} else {
return {
[k.name]: k.value
};
}
})
};
} else {
return {
[i.name]: i.value
};
}
})
};
}
console.log(JSON.stringify(compiled, 0, 2));
I should add, this is intended to eventually also apply validation and grab real data when it gets to the string objects.
The output looks like this:
{
"REPORT": [
{
"SEGMENT0": [
{
"NUMBER1": ""
},
{
"NUMBER2": ""
}
]
},
{
"SEGMENT1": [
{
"RECORD1": [
{
"NUMBER1": ""
},
{
"NUMBER2": ""
}
]
}
]
},
{
"SEGMENT2": []
},
{
"SEGMENT3": []
},
{
"SEGMENT4": []
},
{
"SEGMENT5": []
}
]
}
You could destructure the object, get name, ele and value and return a new object with name as key and either an array by mapping the objects of ele or the value.
const
getData = ({ name, ele, value }) => ({
[name]: Array.isArray(ele)
? ele.map(getData)
: value
});
var scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0\'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root,
result = getData(scheme.ele);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina's answer is cleaner but this looks a bit more like your code so I figured I'd post it anyway.
let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":"1"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"2"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let newScheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":"1"},{"name":"NUMBER2","value":"3"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"4"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
//Yay, recursion!
function mapObj(a, o = {}) {
let array = o[a.name] || [];
for (let i = 0; i < a.ele.length; i++) {
let b = a.ele[i];
array[i] = b.ele ?
mapObj(b, array[i]) : {
[b.name]: b.value
};
}
o[a.name] = array;
return o;
}
let obj = mapObj(scheme.ele);
console.log(obj);
console.log(mapObj(newScheme.ele, obj));

how to get keys of nested object

If I have a flat object then this works:
let stateCopy={...this.state}
Object.entries(dictionary).map(([key,value])=>{
stateCopy.key = value.toString())
})
Is there a way to do this if dictionary contains a nested object. Suppose a dictionary looks like:
dictionary={left:{name:'WORK',
min:2,
sec:0,}
start:true}
I need some way of updating stateCopy, i.e
stateCopy.left.name='WORK'
stateCopy.left.min=2
stateCopy.left.sec=0
stateCopy.start=true
function flattenDictionary(dict) {
if (!dict) {
return {};
}
/** This will hold the flattened keys/values */
const keys = {};
// Perform the flatten
flattenH(dict);
return keys;
function flattenH(obj, prefix) {
Object.keys(obj).forEach((key) => {
const val = obj[key];
/** This is what we pass forward as a new prefix, or is the flattened key */
let passKey;
// Only expect to see this when the original dictionary is passed as `obj`
if (!prefix || prefix === '') {
passKey = key;
} else {
// "Ignore" keys that are empty strings
passKey = ((key === '') ? prefix : `${prefix}.${key}`);
}
if (typeof obj[key] !== 'object') {
keys[passKey] = val;
} else {
flattenH(val, passKey);
}
});
}
}
Seems like you can do this with a little recursive function:
let state = {
left:{
start: "mark",
anotherLevel: {
test: 'leveltest'
}
},
test: "will be replaced"
}
let dictionary={
test2: {
foo: 'bar'
},
left:{
name:'WORK',
min:2,
sec:0,
anotherLevel: {
test_add: 'leveltest_add'
}
},
start:true,
test: 'replaced with me'
}
let stateCopy={...state}
function merge(obj, dict){
Object.entries(dict).forEach(([k, v]) =>{
if (!obj[k] || typeof v !== 'object') obj[k] = v
else merge(obj[k], v)
})
}
merge(stateCopy, dictionary)
console.log(stateCopy)

JavaScript - Performing a recursive search, value not being retained

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
}

Categories

Resources