I have a data structure that looks like this:
{
sections: [
{
pages: [
{
questions: [
],
},
],
},
],
}
There's data in the questions array that I'm trying to get at, and return a final 1 dimensional array at the end. There can be an x number of sections, each section can have an x number of pages, and each page can have an x number of questions.
I'm trying to keep the code relatively concise but also readable, this is my current implementation:
function generateQuestionData(survey) {
let data = [];
survey.sections.forEach((section) => {
section.pages.forEach((page) => {
const newData = page.questions.map(getQuestionDataItem);
data = [...data, ...newData];
});
});
return data;
}
EDIT
Is there a way to accomplish the same thing without the data variable reassignment? so something along the lines of
function generateQuestionData(survey) {
return survey.sections.forEach((section) => { // something instead of forEach
section.pages.forEach((page) => page.questions.map(getQuestionDataItem));
// data = [...data, ...newData]; no need for this?
});
}
if that makes sense
How about having a helper for iteration:
function* goOver(array, key, ...keys) {
for(const el of array) {
if(keys.length) {
yield* goOver(el[key] || [], ...keys);
} else {
yield el[key];
}
}
That can be used as:
const result = [...goOver(survey.sections, "pages", "questions")]
.map(getQuestionDataItem);
concerning the second question:
Is there a way to accomplish the same thing without the data variable reassignment?
how about:
data.push(...newData);
You could use reduce in order to show the intent to really do something per section and page to the likes of:
private generateQuestionData = (survey) =>
survey.sections.reduce((data, section) =>
[
...data,
...section.pages.reduce((pageData, page) =>
[
...pageData,
...page.questions.map(getQuestionDataItem)
],
[]
)
],
[]
);
Code indentation/formatting could surely be optimized.
const data = {
sections: [
{
pages: [
{
questions: [
"hello",
"world"
],
},
{
questions: [
"foo",
"bar"
]
}
],
},
],
};
const get = (...pathKeys) => o => pathKeys.reduce((o,k ) => o[k], o);
const flatten = (arrays) => [].concat.apply([], arrays);
const result = flatten(get("sections")(data).map(s => get("pages")(s).map(get("questions"))));
console.log(flatten(flatten(result)));
Note that the flatten at the end could be simplified with a flattenDeep function.
In "pseudo code" I would express your problem like that, with
compose being composition left to right, and map being fn => arr => arr.map(fn)
compose(
get("section"),
map(compose(get("pages"), map(get("questions")))),
flattenDeep,
);
Related
I am trying to create a new array.
I have list of plugins with different versions, and I need to find plugins with same uniqueIdentifier but different versions, and create one array out of it.
Example:
{
"uniqueIdentifier": "theme_test",
"version": "2011120800"
},
{
"uniqueIdentifier": "theme_test",
"version": "3011120800"
},
{
"uniqueIdentifier": "theme_test",
"version": "4011120800"
},
to be like this:
{
"uniqueIdentifier": "theme_test",
"version": [
"2011120800",
"3011120800",
"4011120800"
]
}
So, in my code I am getting all the information, but I cannot make it work to store this versions as an array. So I am checking the uniqueIdentifier and then the version, and trying to generate new array:
item.pluginVersions.items.forEach(function(plugin) {
pluginVersionsSupported.forEach(function(supportedPlugin) {
if (plugin.uniqueIdentifier === supportedPlugin.uniqueIdentifier) {
if (plugin.version == supportedPlugin.version) {
pluginData = {
uniqueIdentifier: plugin.uniqueIdentifier,
version: []// need to update this to be an array
}
}
}
})
I appreciate all the help.
you need to use Array.reduce method:
const data =
[ { uniqueIdentifier: 'theme_test', version: '2011120800' }
, { uniqueIdentifier: 'theme_test', version: '3011120800' }
, { uniqueIdentifier: 'theme_test', version: '4011120800' }
]
const result = Object.values(data.reduce( (r,{uniqueIdentifier,version}) =>
{
r[uniqueIdentifier] ??= { uniqueIdentifier, version:[] }
r[uniqueIdentifier].version.push(version)
return r
},{}))
console.log(result)
Assuming you only have one pluginData: move the new object out of the loop so that it doesn't get created repeatedly, and in the loop push the version to the existing array.
pluginData = {
uniqueIdentifier: plugin.uniqueIdentifier,
version: []
}
item.pluginVersions.items.forEach(function(plugin) {
...
if (plugin.version == supportedPlugin.version) {
pluginData.version.push(plugin.version);
You may also use Array#reduce() and Object.entries() as follows:
const data = [{"uniqueIdentifier": "theme_test","version": "2011120800"},{"uniqueIdentifier": "theme_test","version": "3011120800"},{"uniqueIdentifier": "theme_test","version": "4011120800"}];
const groupedData = Object.entries(
data.reduce(
(prev, {uniqueIdentifier,version}) =>
({ ...prev, [uniqueIdentifier]:(prev[uniqueIdentifier] || []).concat(version) }), {}
)
)
.map(([uniqueIdentifier,version]) => ({uniqueIdentifier,version}));
console.log( groupedData );
I'm trying to convert an object into single array with just key and value. I tried two different ways. Though I succeeded in my first attempt but using reduce I am unable to get the result. Please kindly let me know if there is/are better ways of achieving the same. Thank you.
var demoArr = [
{
"results": [
{
"listing_id": 10544193
},
{
"listing_id": 4535435
}
],
"results1": [
{
"listing_id": 1054419363
},
{
"listing_id": 432535435
}
]
}
];
let aaa = [];
// demoArr.map(x => { (before)
demoArr.forEach(x => { //(after)
let ss = Object.values(x);
// ss.map(y => { (before)
ss.forEach(y => { //(after)
y.forEach(k => {
aaa.push({"listing_id" : k.listing_id});
})
});
});
Resulted in the following.
[{"listing_id":10544193},{"listing_id":4535435},{"listing_id":1054419363},{"listing_id":432535435}]
Is there a better way to achieve the above? Maybe by using reduce? I tried but failed to get the result.
var ds = demoArr.reduce((a,value) => {
a[value.listing_id] = a[value.listing_id] ? a[value.listing_id] : value
return a
},{});
Here's one way use flatMap() and Object.values, then flatten the result. Using Object.values as an argument in this fashion is a shorthand for applying the map argument directly into the Object.values method and returning the result
var demoArr = [{
"results": [{
"listing_id": 10544193
},
{
"listing_id": 4535435
}
],
"results1": [{
"listing_id": 1054419363
},
{
"listing_id": 432535435
}
]
}];
let output = demoArr.flatMap(Object.values).flat();
console.log(output)
i'm new here, i have problem that i can not solve.
I have 2 different arrays:
The first array - contains ratings of users with their ID name
[
{"handle":"frontend1", "_redis":"3", "_nodejs":"5", "_mysql":"2", "_python":"3", "_mongo":"4"},
{"handle":"frontend3", "_php":"4", "_mysql":"4", "_oracle":"4", "_ruby":"3", "_mongo":"5", "_python":"5"},
{"handle":"frontend4", "_java":"5", "_ruby":"5", "_mysql":"5", "_mongo":"5"}
]
The second set - contains the ratings, which I want to return to each user.
If there is a rating that is not in the second set, I will not return it
In the second set, values do not matter, only keys
[
"_assembler",
"_css",
"_python",
"_php"
]
I want to return to the first set, the handle, and all the rankings that exist in the second set.
[
{"handle":"frontend1", "_python":"3" },
{"handle":"frontend3", "_php":"4", "_python":"5" },
{"handle":"frontend4"}
]
this is what i try to do.
keys = [
"_assembler",
"_css",
"_python",
"_php"
]
source = [
{"handle":"frontend1", "_redis":"3", "_nodejs":"5", "_mysql":"2", "_python":"3", "_mongo":"4"},
{"handle":"frontend3", "_php":"4", "_mysql":"4", "_oracle":"4", "_ruby":"3", "_mongo":"5", "_python":"5"},
{"handle":"frontend4", "_java":"5", "_ruby":"5", "_mysql":"5", "_mongo":"5"}
];
result = [];
tmp = {};
source.forEach((item) => {
Object.keys(item).map(({key,value}) =>
{
if(key == "handle")
{
tmp[key]=value;
}
if(keys.includes(key))
{
tmp[key]=value;
}
})
result.push(...tmp);
tmp = {};
});
You can do this with a map utilizing a couple of other array methods such as filter, and Object methods.
const keys = [
"_assembler",
"_css",
"_python",
"_php"
]
const source = [
{"handle":"frontend1", "_redis":"3", "_nodejs":"5", "_mysql":"2", "_python":"3", "_mongo":"4"},
{"handle":"frontend3", "_php":"4", "_mysql":"4", "_oracle":"4", "_ruby":"3", "_mongo":"5", "_python":"5"},
{"handle":"frontend4", "_java":"5", "_ruby":"5", "_mysql":"5", "_mongo":"5"}
];
const result = source.map( s => ({
handle: s.handle,
...Object.fromEntries(Object.entries(s).filter(x => x[0] != "handle" && keys.includes(x[0])))
}));
console.log(result);
So,
I am receiving the data that has the following information:
{
"data":[
{
"vote_count":22222,
"id":299537,
"ready":false,
},
{
"vote_count":2850,
"id":299534,
"ready":true,
},
]
}
Now I need to make a new object that would contain the same structure but with some properties, ie:
{
"data": [
{
"ready":false,
},
{
"ready":true,
}
]
}
I need the solution that is scalable, imagine having a set of data with 50 properties for example. Also, I did find solutions with objects, but never with array of objects.
Thanks guys, I've been busting my head for three hours now.
You could use destrcuturing and shorthand property names to create new objects like this:
const input={"data":[{"vote_count":22222,"id":299537,"ready":false,},{"vote_count":2850,"id":299534,"ready":true,},]}
const data = input.data.map(({ ready }) => ({ ready }))
console.log({ data })
If you want to get a bunch of properties, you could create an array of properties you need. Then use Object.assign() or reduce to create a subset of each object like this:
const input={"data":[{"vote_count":22222,"id":299537,"ready":false,},{"vote_count":2850,"id":299534,"ready":true,},]}
const properties = ["vote_count", "ready"]
const data = input.data.map(a =>
Object.assign({}, ...properties.map(p => ({ [p]: a[p] })))
)
/* You could also use reduce like this:
input.data.map(a => properties.reduce((r, p) => ({ ...r, [p]: a[p] }), {}))
*/
console.log({ data })
Map the properties you want
var obj1 = {
"data":[
{
"vote_count":22222,
"id":299537,
"ready":false,
},
{
"vote_count":2850,
"id":299534,
"ready":true,
},
]
}
var obj2 = {}
obj2.date = obj1.data.map(data => ({ ready: data.ready}));
console.log(obj2)
You can do it using Array#map method and Array#reduce method
const input = {
"data": [{
"vote_count": 22222,
"id": 299537,
"ready": false,
},
{
"vote_count": 2850,
"id": 299534,
"ready": true,
},
]
}
const extract = ['ready']
const data = input.data.map(o => extract.reduce((obj, k) => (obj[k] = o[k], obj), {}))
console.log({ data })
I'm trying to filter an array of objects which has objects of array of objects inside. For example, an object in an array would look like this.
list=[...,
{
"types": [
{
"slot": 2,
"type":
{
"url": "http://pokeapi.co/api/v2/type/4/",
"name": "poison"
}
},
{
"slot": 1,
"type":
{
"url": "http://pokeapi.co/api/v2/type/12/",
"name": "grass"
}
}
],
"name": 'bulbasaur'
},...
]
I'm currently filtering the list by its name and the types of objects like this (the this.props.search.search being a string, and the example being an example list of strings that will be adjusted):
let filtered = this.props.pokemons.filter((pokemon)=>{
return pokemon.name.toLowerCase().indexOf(this.props.search.search.toLocaleLowerCase())!==-1;
})
let example = ['fire', 'ice', 'water'];
let filteredx= filtered.filter((pokemon)=>{
return pokemon.types.filter((type)=>{
return example.indexOf(type.type.name)!==-1
}).length>0
})
Is there a method of combining all the filters into one instead of calling
array.filter(...).filter(...)
As in the future, if more filters are added, I'm afraid that it's going to end up looking like
array.filter(...).filter(...).filter(...).filter(...).filter(...)
Any help would be appreciated.
You can combine the two conditions with an &&:
let filteredx = this.props.pokemons.filter(pokemon =>
pokemon.name.toLowerCase().includes(this.props.search.search.toLocaleLowerCase())
&& pokemon.types.some(type => example.includes(type.type.name))
)
Note you can use includes and some in your conditions, and use the expression syntax in your arrow functions (without braces nor return).
You can add more conditions with additional && operators. Make sure to put them in such order that the most simple conditions (that require least work) come first.
If the array is small and perfomance not an issue;
const arryOfPokemons = [{name: 'name1', type: 'type1'}];
function name(pokemon) { return pokemon.name !== 'name' }
function type(pokemon) { return pokemon.type !== 'type' }
const result = [name, type].reduce((result, filterFunc) => result.filter(filterFunc), arryOfPokemons);
otherwise you can try to combine the conditions into the same filter function.
Instead of filtering multiple times, you can combine all the filter conditions in one filter.
Instead of doing this...
let filtered1 = toFilter.filter((element) => {
return condition1;
});
let filtered2 = filtered1.filter((element) => {
return condition2;
});
...
let filteredN = filteredN_1.filter((element) => {
return conditionN;
});
... you can combine the conditions in a single filter:
let filtered = toFilter.filter((element) => {
return condition1 && condition2 && ... && conditionN;
});
If one of the conditions is very long, you can easily abstract it in a separate function. This also makes the code more readable and maintainable.
let filtered = toFilter.filter((element) => {
const condition1 = computeCondition1(arg1, arg2);
const condition2 = computeCondition2(arg1);
...
const condition3 = computeCondition3(arg2, arg3, arg4);
return condition1 && condition2 && ... && conditionN;
});
You could define an object which contains a property for every first level property of your pokemon list you want to test. The value would be a predicate with the "test logic" for this property.
const pokemons = [
{"name":"poke1","types":[{"type":{"name":"poison"}},{"type":{"name":"grass"}}]},
{"name":"poke2","types":[{"type":{"name":"fire"}},{"type":{"name":"grass"}}]},
{"name":"poke3","types":[{"type":{"name":"ice"}},{"type":{"name":"water"}}]},
{"name":"poke4","types":[{"type":{"name":"ice"}},{"type":{"name":"grass"}}]}
];
const filterOptions = {
name: (name) => {
return ["poke1", "poke5"].some(x => x === name);
},
types: (types) => {
return ["ice", "water"].some(t => types.some(x => t === x.type.name));
}
};
function filterList(list, options) {
return list.filter(pokemon => {
return Object.keys(options)
.some(key => {
if (key in pokemon) {
return filterOptions[key](pokemon[key]);
}
});
});
}
const filtered = filterList(pokemons, filterOptions);
filtered.forEach(p => console.log(JSON.stringify(p)));