How to select nested object's property with map() in Javascript? - javascript

This is my object.
values : {
title : 'this is title ..',
translate : {
en : 'some texts .. ',
},
}
And this is array I have.
arr = ['translate.en', 'title'];
What I want to do is this, but this is not work as I expected.
const value = arr.map(item => values[item]);
// (Result) value : undefined, 'this is title ..'
// (Expected) value : 'some texts .. ', 'this is title ..'
const value = arr.map(item => values.item); // error : 'item' is declared but its value is never read.
How to get values.translate.en value using map()?

you can use this snippet. Not the cleanest, but you get the idea
arr.map(item => {
let arrItem = item.split('.');
let data = values;
for (i in arrItem) {
data = data[arrItem[i]];
}
return data;
});

Your current code is doing the following:
const values = {
title: 'this is title ..',
translate: {
en: 'some texts .. ',
},
'translate.en': 'hello',
};
const arr = ['translate.en', 'title'];
const value = arr.map((item) => values[item]);
// (Result) value : 'hello', 'this is title ..'
If you want to do what you want to do, you should do the following:
const values = {
title: 'this is title...',
translate: {
en: 'some texts... ',
},
};
const arr = ['translate.en', 'title'];
const value = arr.map((item) => {
let index = item.split('.');
let value = values;
for (i in index) {
value = value[index[i]];
}
return value;
});
// (Result) value : 'come texts...', 'this is title ..'
However, if possible, I would suggest structuring your values object in the following way:
const values = {
title: {
kr: '타이틀',
en: 'title'
},
body: {
en: 'some texts .. ',
kr: '어쩌고 저쩌고 .. ',
},
};
So as long as you know which part: body or title is required and you know the language you want to get, it will be easy to get it.

Related

access the properties and their values of an object dynamically in JavaScript

I wanna access the properties and their values of an object dynamically, the scenario is:
const SoldeN = {
1:4,
}
const SoldeN_1 = {
2:5,
}
const soldes = [
{ formula: '=SoldeN("1")',value:0},
{ formula: '=SoldeN_1("2")',value:0},
{ formula: '=SoldeN_1("1")+SoldeN_1("2")',value:0},
];
What I've tried so far:
const getProperty = string => string.match(/\(.*?\)/g).map(x => x.replace(/[(")]/g, ''))
const getObject = string => string.match(/[=+](.*?)\(/g).map(x => x.replace(/[=(+]/g, ''))
console.log(soldes.map(solde=>[getObject(solde.formula),getProperty(solde.formula)]))
[
[["SoldeN"], ["1"]],
[["SoldeN_1"], ["2"]],
[
["SoldeN_1", "SoldeN_1"],
["1", "2"],
],
];
Till now Everything is ok, but To get the value of an object dynamically based on property I used this function:
const getPropertyValue = (obj, prop) => obj[prop];
getPropertyValue('SoldeN','1')
//'o'
It gives me 'o' instead of 1, I know If I've passed the reference of an object it bring its value; but to get it dynamically I've to pass the actual name of an object which is string, not the object reference.
The expected Result would be:
result = [
{ formula: '=SoldeN("1")',value:4},
{ formula: '=SoldeN_1("2")',value:5},
{ formula: '=SoldeN_1("1")+SoldeN_1("2")',value:9},
];
Why don't you just put your data inside an object and map the data inside loop?
There is no need to use eval at all.
const data = {
SoldeN: {
1: 4,
},
SoldeN_1: {
2: 5,
},
};
const soldes = [
{ formula: '=SoldeN("1")', value: 0 },
{ formula: '=SoldeN_1("2")', value: 0 },
{ formula: '=SoldeN("1")+SoldeN_1("2")', value: 0 },
];
const getProperty = string =>
string.match(/\(.*?\)/g).map(x => x.replace(/[(")]/g, ""));
const getObject = string =>
string.match(/[=+](.*?)\(/g).map(x => x.replace(/[=(+]/g, ""));
const output = soldes.map(solde => {
const objs = getObject(solde.formula);
const props = getProperty(solde.formula);
const newVal = objs.reduce((carry, item, idx) => {
carry += data[item][props[idx]];
return carry;
}, solde.value);
return {
...solde,
value: newVal,
};
});
console.log(output);
You can eval the object name.
const SoldeN = { 1:4 };
const getPropertyValue = (obj, prop) => eval(obj)[prop];
console.log(getPropertyValue('SoldeN','1'));
But be cautious as it is risky! If bad code can get into the eval argument, things can happen.
You can make use of javascript eval method to execute the expression.
Logic
Loop through nodes in soldes.
Replace all "=" with empty string. "(" with "[" and ")" with "]".
This will mmake the expression =SoldeN("1")+SoldeN_1("2") to SoldeN["1"]+SoldeN_1["2"].
Evaluating this will give you the expected result.
const SoldeN = {
1: 4,
};
const SoldeN_1 = {
2: 5,
};
const soldes = [
{ formula: '=SoldeN("1")', value: 0 },
{ formula: '=SoldeN_1("2")', value: 0 },
{ formula: '=SoldeN("1")+SoldeN_1("2")', value: 0 },
];
const getValue = (formula) => eval(formula.replaceAll("=", "").replaceAll("(", "[").replaceAll(")", "]"));
const result = soldes.map((item) => ({
formula: item.formula,
value: getValue(item.formula)
}));
console.log(result);
Please Note Error handling is considered as out of scope.

Function is getting called multiple times

I have an array of objects
const data = [{
Description: "confirm"
Id: "1"
Name: "confirm"
Value: "VIP:confirm"
}, {
Description: "validate"
Id: "2"
Name: "validate"
Value: "VIP:validate"
}, {
Description: "Sent"
Id: "2"
Name: "Sent"
Value: "VIP:Sent"
}]
Now, I am trying to get the description by passing the value:
const valuesObject = [
"VIP:Confirmed",
"VIP:Validated",
"VIP:Sent"
]
Now, Values data is like
const getDescription = (
value: string,
Values: Array < >
) => {
let allValues = _.find(Values, item => item.Value === value)
return resolve(allValues)
}
const resolve = (object) => {
return object?.Description ? object.Description : object?.Name ?? ''
}
Now, here I am doing ,
const status = valuesObject.map((value) => {
return getDescription(value, data)
})
return status.join('/')
I was expecting it should return me Confirmed/Validated/Sent
It returns but function gets called multiple times. can any one help me with this ?
Use _.intersectionWith() to get objects with the Value property that matches one of an array of values. Then map to get the Description or Name:
const getDescription = (arr, values) => _.map(
_.intersectionWith(arr, values, (o, v) => o.Value === v), // get all objects with matching values
({ Description, Name = '' }) => Description || Name // map to description / name / empty string
).join('/')
const data = [{"Description":"Confirmed","Id":"1","Name":"confirm","Value":"VIP:Confirmed"},{"Description":"Validated","Id":"2","Name":"validate","Value":"VIP:Validated"},{"Description":"Sent","Id":"2","Name":"Sent","Value":"VIP:Sent"}]
const valuesObject = ["VIP:Confirmed","VIP:Validated","VIP:Sent"]
const result = getDescription(data, valuesObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.js"></script>
Using lodash/fp you can generate the getDescription() function with _.flow():
const getDescription = _.flow(
_.intersectionWith((o, v) => o.Value === v), // get all objects with matching values
_.map(({ Description, Name = '' }) => Description || Name), // map to description / name / empty string
_.join('/')
)
const data = [{"Description":"Confirmed","Id":"1","Name":"confirm","Value":"VIP:Confirmed"},{"Description":"Validated","Id":"2","Name":"validate","Value":"VIP:Validated"},{"Description":"Sent","Id":"2","Name":"Sent","Value":"VIP:Sent"}]
const valuesObject = ["VIP:Confirmed","VIP:Validated","VIP:Sent"]
const result = getDescription(data, valuesObject)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>

Can we use .filter() function with conditional rules for the searched properties?

Let's assume that we have the following .filter function:
search(searchQuery: string) {
let results = myArray.filter(item =>
item.title.toLowerCase().includes(searchQuery) ||
item.description.toLowerCase().includes(searchQuery)
);
return results;
}
For this example we can consider that item doesn't have a description property always but only some times.
In this case function will fail with an error:
ERROR TypeError: Cannot read property 'toLowerCase' of undefined
How can we can still search in the array and include the description property in the search only if it exists? Is this possible within the .filter() function?
You could take a default value.
Either directly
(item.description || '').toLowerCase().includes(searchQuery)
or take a destructuring with a default values.
results = myArray.filter(({ title = '', description = '' }) =>
title.toLowerCase().includes(searchQuery) ||
description.toLowerCase().includes(searchQuery)
);
You can do something like this:
const myArray = [
{
title: 'Test 123',
},
{
title: 'Test 123',
description: 'test',
},
{
title: 'What',
},
{
title: 'Fix',
description: '456123',
},
];
const search = (text) => {
let results = myArray.filter(item => {
if (item.description) {
return item.title.toLowerCase().includes(text) || item.description.toLowerCase().includes(text);
}
});
return results;
}
const results = search('test');
console.log(results);
search(searchQuery: string) {
return myarray.filter(item => 'description' in item
? item.description.toLowerCase().includes(searchQuery)
: item.title.toLowerCase().includes(searchQuery)
);
}
Might want to do the same check for title too but how far down do you go.

Filter data inside array object of array object using javascript

I am trying to filter data inside array object of array object, Please find below code for more information.
var data = [
{
name:'testdata1',
subdata:[{status:'fail'},{status:'success'}]
},
{
name:'testdata2',
subdata:[{status:'fail'},{status:'success'}]
}
]
Expected Data:
var successdata = [
{
name:'testdata1',
subdata:[status:'success'}]
},
{
name:'testdata2',
subdata:[status:'success'}]
}
];
var FailureData =[
{
name:'testdata1',
subdata:[{status:'fail'}]
},
{
name:'testdata2',
subdata:[{status:'fail'}]
}
];
I missed curly braces,So i am updating
Hope this helps.
const data = [{
name: 'testdata1', subdata: [{status: 'fail'}, {
status:
'success'
}]
},
{
name: 'testdata2', subdata:
[{status: 'success'}, {status: 'fail'}]
}
];
const filterData = (data, status) => data.reduce((acc, val) => {
const sub = val.subdata.map((v) => v.status === status ? ({ name: val.name, subdata: [v] }) : null).filter(f => f !== null);
return acc.concat(sub);
}, []);
const successData = filterData(data, 'success');
const failureData = filterData(data, 'fail');
console.log('successData', successData);
console.log('failureData', failureData);
You could map your arrays using Array.map():
var successData = data.map(item => ({name: item.name, subdata:[{status:'success'}]})
What I guess you want to do is filter the array based on subdata status.
I also guess that what subdata should have is just the status property and your code would be: var data = [{name:'testdata1',subdata:[{status:'fail'},{status:'success'}] }.
Then you want to look in the subdata array and find which data have success and failure in them.
So what you could be looking for is this:
var successData = data.filter(sdata => {
var successFlag=false;
sdata.subdata.forEach(subdata=>{
if (subdata.status==='success'){
successFlag = true;
}
}
return successFlag;
}
The same with the failureData.
For more information you could check the Array.prototype.filter function:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
P.S. As mentioned in a comment to your question as well, your subdata array cannot be an object with two of the same property
var data = [{name:'testdata1',subdata:[{status:'fail'}, {status:'success'}] },{name:'testdata2',subdata:[{status:'success'}, {status:'fail'}] }]
var successData = filterByStatus('success', data);
var failureData = filterByStatus('fail', data);
function filterByStatus(status, data) {
return data.map(d => {
var newObj = Object.assign({}, d);
newObj.subdata = newObj.subdata.filter(s => s.status === status);
return newObj;
});
}
console.log('successData', successData);
console.log('failureData', failureData);
one of possible ways to do what you want if you have one success property in your object

How to get proprety value of object from parent object

I have object in this structure:
obj = {
user: { name: 'jeterson' },
title: 'I am a test'
}
I have one key with value: user.name.
I have trying get value like this: obj[key], meaning obj['user.name']. It not works, only works for obj.title.
My object have many values that are also objects, and i want get value like this:
myobject[mykey]
It is possible get value from property object like above ?
You can access it with:
obj['user']['name']
Or alternatively:
obj.user.name
If you want to get from a key like "user.name" to the value, you woulr have to do some logic yourself. You could hack something together like this:
let obj = {
user: {
name: 'jeterson'
},
title: 'I am a test'
}
let key = 'user.name';
let keys = key.split('.');
let res = obj;
while (keys.length > 0 && res) {
let k = keys.shift();
res = res[k];
}
console.log(res) // "jeterson"
When the keys do not match, res holds undefined.
You've got multiple solutions to access an element of an object with its keys:
var obj = {
user: { name: 'jeterson' },
title: 'I am a test'
}
console.log(obj['user']['name']);
console.log(obj['user'].name);
console.log(obj.user['name']);
console.log(obj.user.name);
But you can't do it easily with a variable key = 'user.name'.
If you need to use a variable containing the nested-keys, you could create a function.
Updated answer: An amazingly short way to achieve it is to use .reduce():
// My function
function obj_tree_key(obj, path) {
return path.split('.').reduce((accu, val) => accu[val] || 'Not found', obj);
}
var obj1 = {
user: {
name: 'jeterson'
},
title: 'I am a test'
}
console.log(obj_tree_key(obj1, 'user.name')); // Outputs "jeterson"
// Here is an example with error:
var obj2 = {
user: {
nameeeee: 'jeterson'
},
title: 'I am a test'
}
console.log(obj_tree_key(obj2, 'user.name'));
Old answer: Use a for to loop through the keys and reduce the oject:
// My function
function obj_tree_key(obj, tree_key) {
var result = obj;
var keys = tree_key.split('.');
for (var i = 0; i < keys.length; i++) {
result = result[keys[i]] || 'Not found'; // Error handling
}
return result;
}
var obj1 = {
user: {
name: 'jeterson'
},
title: 'I am a test'
}
console.log(obj_tree_key(obj1, 'user.name')); // Outputs "jeterson"
// Here is an example with error:
var obj2 = {
user: {
nameeeee: 'jeterson'
},
title: 'I am a test'
}
console.log(obj_tree_key(obj2, 'user.name'));
Hope it helps.
first get the user, then the name:
obj['user']['name']
or
obj.user.name
You can also use
obj.user.name
You could access it using
console.log(obj.user.name);
You can do it in 2 way:
obj['user']['name']
or
obj.user.name

Categories