Access and extract values from doubly nested array of objects in JavaScript - javascript

I have an array of objects and within those objects is another object which contains a particular property which I want to get the value from and store in a separate array.
How do I access and store the value from the name property from the data structure below:
pokemon:Object
abilities:Array[2]
0:Object
ability:Object
name:"blaze"
1:Object
ability:Object
name:"solar-power"
How would I return and display the values in the name property as a nice string like
blaze, solar-power ?
I tried doing something like this but I still get an array and I don't want to do a 3rd loop since that is not performant.
let pokemonAbilities = [];
let test = pokemon.abilities.map((poke) =>
Object.fromEntries(
Object.entries(poke).map(([a, b]) => [a, Object.values(b)[0]])
)
);
test.map((t) => pokemonAbilities.push(t.ability));
Sample Data:
"pokemon": {
"abilities": [
{
"ability": {
"name": "friend-guard",
"url": "https://pokeapi.co/api/v2/ability/132/"
},
"ability": {
"name": "Solar-flare",
"url": "https://pokeapi.co/api/v2/ability/132/"
}
}
]
}
Then I am doing a join on the returned array from above to get a formatted string.
It just seems like the multiple map() loops can be optimized but I am unsure how to make it more efficient.
Thank you.

There is no need for a loop within loop. Try this:
const pokemon = {
abilities: [{
ability: {
name: 'friend-guard',
url: 'https://pokeapi.co/api/v2/ability/132/'
},
}, {
ability: {
name: 'Solar-flare',
url: 'https://pokeapi.co/api/v2/ability/132/'
}
}]
};
const pokemonAbilities = pokemon.abilities.map(item => item.ability.name).join(', ');
console.log(pokemonAbilities);

Related

Find matching items in array of object values from array of filter criteria

I have some data (json) that looks like this:
[
{
"name": "Enterprise Networking",
"technology": "Networking"
},
{
"name": "Enterprise Networking",
"technology": "Networking"
},
{
"name": "Wireless Insights",
"technology": "Mobility and Wireless"
},
{
"name": "Mobility Insights",
"technology": "Mobility and Wireless"
},
{
"name": "Lock it down",
"technology": "Security"
},
{
"name": "Collaboration",
"technology": "Security"
}
]
I am trying to find matches based on an array of filtering options for one field named 'technology' in this example. For example, my filtering criteria will be ["Networking", "Security"]. I would expect to return all objects that have 'Networking' and 'Security' as their technology, excluding 'Mobility and Wireless' in this example.
I have figured out how to do it with just one filter criteria using:
result = learningMapsDataObj.filter(el => el.Technology === filter3[0].value);
I've tried adding a method that will loop through the the filter array, but can't get it to work.
result = learningMapsDataObj.filter(el => el.Technology === myloopingArrCheckFunction(el.technology, filters3));
I know I'm close, but haven't had to dev in a while and my multidimensional brain isn't working.
Approaches
You can check if el.technology is included in your array of values to filter by:
const allowedTechnologies = ["Networking", "Security"];
learningMapsDataObj.filter(el => allowedTechnologies.includes(el.technology))
Alternatively, you can also check if el.technology is like some of the values to filter by:
const allowedTechnologies = ["Networking", "Security"];
learningMapsDataObj.filter(el => { // Linebreak (thus function body) for readability
return allowedTechnologies.some(allowedTech => allowedTech === el.technology);
})
Map to string array
It seems your filter is in a form similar to this:
const filter = [
{ value: "Networking" }.
{ value: "Security" }
];
But it would be easier to use if it was in the form as shown in my examples above. You can achieve that by mapping the filter elements to their values:
const filter = [
{ value: "Networking" }.
{ value: "Security" }
];
const filterValues = filter.map(el => el.value);
// Now you can use filterValues like allowedTechnologies
let result = learningMapsDataObj.filter(el => el.technology === "Networking" || el.technology === "Security" );
You can filter the array with an array of criterias like this
const filterCriteria= ["Networking", "Security"]
const filteredArray = learningMapsDataObj.filter((item) => filterCriteria.includes(item.technology));
if you want to set a variable for fields you want to filter, you can also do something like this
const filterField = 'technology'
const filterCriteria= ["Networking", "Security"]
const filteredArray = learningMapsDataObj.filter((item) => filterCriteria.includes(item[filterField]));

how to update the array object using id and the props in javascript

I have an array object and I want to update the array object using id and props.
below is the structure of array object,
array object = [{columGrp:"All",deafultColumnName:"a",id:0},{columGrp:"ll",deafultColumnName:"ww",id:1},{columGrp:"oo",deafultColumnName:"qq",id:2},{columGrp:"qq",deafultColumnName:"ee",id:3}]
I have an editable table design and when a field changes I am passing field name, id, and changed value. based on that how can I update the object array.
const onChange=(props,value,id)=>{
//code here
}
onChange("columGrp","qwerty",1);
// result =>
array object = [{columGrp:"All",deafultColumnName:"a",id:0},{columGrp:"qwerty",deafultColumnName:"ww",id:1},{columGrp:"oo",deafultColumnName:"qq",id:2},{columGrp:"qq",deafultColumnName:"ee",id:3}]
a help would be really appreciable.
You can find the object by id from your array of objects using the filter function. If the object exists, you then change the given property, passing the given value. Objects work by reference in javascript so any changes in the found object will affect your object inside your array too.
const objectsArray = [{
columGrp: "All",
deafultColumnName: "a",
id: 0
}, {
columGrp: "ll",
deafultColumnName: "ww",
id: 1
}, {
columGrp: "oo",
deafultColumnName: "qq",
id: 2
}, {
columGrp: "qq",
deafultColumnName: "ee",
id: 3
}];
const onChange = (props, value, id) => {
const obj = objectsArray.find(x => x.id === id);
if (obj) {
obj[props] = value;
}
}
onChange("columGrp", "qwerty", 1);
console.log(objectsArray)

Alternative to eval for converting a string to an object

I have a function that is using eval to convert a string with an expression to an object based on the parameter.
let indexType = ["Mac", "User", "Line", "Mask", "Ip", "Location"]
const filterIndex = (item) => {
filteredIndexSearch = []
eval(`search${item}`).forEach((e) => filteredIndexSearch.push(searchData[e.key]))
}
filterIndex(indexType[searchTotal.indexOf(Math.max(...searchTotal))])
searchData is an array that returns values based on the user input.
searchTotal is an array with the length of each search{item} array.
The filterIndex function takes the highest value from the searchData array and corresponds it to the indexType array, then use eval to convert the string to an object to pass the value to the filteredIndexSearch array.
What would be a better alternative to eval?
EDIT
To add more information on what this does:
searchData = [
[
{
key: 1,
data: "0123456789101"
},
{
key: 1,
data: "John Smith"
}
],
[
{
key: 2,
data: "0123456789102"
},
{
key: 2,
data: "Jane Smith"
},
]
]
const search = (data, key, container) => {
if (!data) data = "";
if (data.toLowerCase().includes(string)) {
container = container[container.length] = {
key: key,
data: data
}
}
}
const returnSearch = () => {
for (let i = 0; i < searchData.length; i++) {
search(searchData[i][0].data, searchData[i][0].key, searchMac)
search(searchData[i][1].data, searchData[i][1].key, searchUser)
}
}
returnSearch()
The data is incomplete, but hopefully conveys what I'm trying to do.
search will take the user input, and store the information in the corresponding array. If I input "Jo", it will return the searchUser array with only the "John Smith" value and all the other values with the same key. Inputting "102" returns the searchMac with the "0123456789102" value and all other values with the same key.
At the end of the day. I just want to convert search${parameter} to an object without using eval.
Move your global arrays into an object.
Somewhere it appears that you're defining the arrays, something like:
searchMac = [...];
searchUser = [...];
...
Instead of defining them as individual arrays, I'd define them as properties in an object:
searchIndices.Mac = [...];
searchIndices.User = [...];
...
Then, instead of using eval, your can replace your eval().forEach with searchIndices[item].forEach.
If the order of your search isn't important, your can instead loop through the keys of searchIndices:
Object.keys(searchIndices).forEach(item => {
searchIndices[item].forEach(...);
});
This ensures that if you ever add or drop an entry in searchIndices, you won't miss it or accidentally error out on an undefined search index.
Any time you have a situation with variables named x0, x1 etc, that should be a red flag to tell you you should be using an array instead. Variable names should never be semantically meaningful - that is code should never rely on the name of a variable to determine how the code behaves. Convert search0 etc into an array of search terms. Then use:
const filterIndex = (item) => search[item].map(i => searchData[i.key]);
filteredIndexSearch = filterIndex(indexType[searchTotal.indexOf(Math.max(...searchTotal))]);
(simplifying your code). Note that in your code, filteredIndexSearch is modified inside the arrow function. Better to have it return the result as above.

From single array convert to an array of object with keys coming from a JSON response -JAVASCRIPT-

I am receiving a json response from an API call. I need to store its keys, and create an array of an object. I am intending to this array of an object is created dynamically no matter the keys of the response.
I've already got the keys like this:
const json_getAllKeys = data => {
const keys = data.reduce((keys, obj) => (
keys.concat(Object.keys(obj).filter(key => (
keys.indexOf(key) === -1))
)
), [])
return keys
}
That returned an array (using a sample json):
['name','username', 'email']
But I am trying to use that array to create an array of object that looks like this one
[
{
name: "name",
username: "username",
email: "Email",
}
];
I've been trying mapping the array, but got multiple objects because of the loop, and I need a single one to make it work.
keys.map(i=>({i:i}))
[
{ i: 'id' },
{ i: 'name' },
{ i: 'username' },
{ i: 'email' }
]
Any hint would be useful!
Thanks in advance :D
What you're looking for is Object.fromEntries, which is ECMA2019, I believe, so available in Node >=14 and will be provided as a polyfill if you employ babel.
I can't quite discern what your reduce should produce, but given the sample input, I would write
const input = ['name','username', 'email'];
const result = Object.fromEntries(input.map(name => ([name, name])));
// result == { name: 'name', username: 'username', email: 'email' }
You're definitely on the right track. One thing to remember is the map function will return the SAME number of output as input. So in your example, an array of 3 returns an array of 3 items.
For this reason, map alone is not going to give you what you want. You may be able to map => reduce it. However, here is a way using forEach instead. This isn't a strictly functional programming style solution, but is pretty straight forward and depending on use case, probably good enough.
let keys = ['name','username', 'email'] //you have this array
const obj = {}; // empty object to hold result
keys.forEach(i => {
obj[i] = i; // set the object as you want
})
console.log(obj); // log out the mutated object
// { name: 'name', username: 'username', email: 'email' }

JavaScript object array filtering with embedded array

I have an object that looks like the following:
let responseData = [
{
"name": "name",
"other": "value",
"anotherField": "blue",
"appRoles": [
{
"code": "roleOne",
"shouldDisplay": true
},
{
"code": "roleTwo",
"shouldDisplay": false
}
]
}
I need to maintain the original structure all while keeping existing properties. I only want to remove/filter out any "appRoles" where "shouldDisplay" is false.
The following works, using a forEach and a filter operation to create a new object array, but is it possible to condense this even more?
let filteredApps;
responseData.forEach((team) => {
let indyTeam = team;
indyTeam.appRoles = team.appRoles.filter((role) => role.shouldDisplay === true);
filteredApps.push(indyTeam);
});
When I use the map operation, I only get an array of the filtered appRoles - missing extra properties on each object such as "name":
let enabledAppRolesOnly =
responseData.map((team) =>
team.appRoles.filter((role) => role.shouldDisplay === true));
array.map function calls a callback for each element of your array, and then push the return value of it to a new array.
from MDN doc:
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
So in your case, since you return team.appRoles.filter((role) => role.displayByDefault === true) which is your team array, you only get this.
What you could do would be this (in order to fully clone the object):
let responseData = [{
"name": "name",
"appRoles": [
{
"code": "roleOne",
"shouldDisplay": true
},
{
"code": "roleTwo",
"shouldDisplay": false
}
]
}]
let enabledAppRolesOnly = responseData.map(team => {
const appRoles = team.appRoles.filter(role => role.shouldDisplay === true)
return Object.assign({}, team, { appRoles })
});
console.log(enabledAppRolesOnly)
This will achieve your objective non-destructively. It will build a new array for you.
let responseData = [{
name: "name",
appRoles: [{
code: "roleOne",
shouldDisplay: true
}, {
code: "roleTwo",
shouldDisplay: false
}]
}];
let output = responseData.map(o => Object.assign({}, o, {
appRoles: o.appRoles.filter(r => r.shouldDisplay)
}));
console.log(responseData);
console.log(output);
Code explanation -
map
The map function iterates over the whole array and modifying the each item as specified this should be self evident.
Object.assign
This could be the tricky part -
o=>Object.assign({}, o, {appRoles: o.appRoles.filter(r=>r.shouldDisplay)})
From the docs Object.assign is used to copy values from the object.
The first argument {} causes a new object to be created.
The second argument o causes all props from the object o to be copied in the newly created object.
Now, note that we need to modify the appRoles property and keep only those roles which have shouldDisplay as true. That's exactly what the third argument does. It modifies the appRoles property and gives it the new value.
filter
Now the code -
o.appRoles.filter(r=>r.shouldDisplay)
should not be too difficult.
Here we keep only those roles which meet our criterion (namely shouldDisplay should be true)
If you look at the filter function, it expects the callback value to return a boolean value on whose basis it determines whether value has to be kept or not.
So the following code is not even required,
o.appRoles.filter(r=>r.shouldDisplay===true)
This is enough,
o.appRoles.filter(r=>r.shouldDisplay)
There's some missing information in the question. I'll assume the following:
filteredApps should only contain items where there's at least one appRole for display
it is OK if responseData and filteredApps contains references to the same team objects
there are no other references to team objects that need to keep the original data unaffected
As such, you can reduce your code down to this:
let filteredApps = responseData.filter(team =>
(team.appRoles = team.appRoles.filter(role => role.shouldDisplay)).length;
);
The result will be that each team will have only the .shouldDisplay members in its appRoles, and filteredApps will only have teams with at least one appRole with shouldDisplay being true.
You could build a new array with only part who are valid chinldren elements.
let responseData = [{ name: "name", appRoles: [{ code: "roleOne", shouldDisplay: true }, { code: "roleTwo", shouldDisplay: false }] }],
result = responseData.reduce((r, a) => {
var t = a.appRoles.filter(o => o.shouldDisplay);
if (t.length) {
r.push(Object.assign({}, a, { appRoles: t }));
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources