Lodash reduce array of objects from array of objects - lodash

I'm trying to get a list of all guideScenes in all sections via
let combinedScenes = _.reduce(sections,
(prev, section) => {return {...prev, ...section.guideScenes}},
{}
)
It's only returning the first section list, how do I get all of them in there?

const sections = [{guideScenes: ['hello']}, {guideScenes: ['world']}, {guideScenes: ['hi', 'yo']}]
let combinedScenesObj = _.reduce(sections, (prev, section, index) => {
return {...prev, [index]: [...section.guideScenes]}
}, {})
console.log('object:', combinedScenesObj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.core.js"></script>
You were close you needed a key to be added to each array because you were trying to add to an object
//dummy data
const sections = [{guideScenes: ['hello']}, {guideScenes: ['world']}, {guideScenes: ['hi', 'yo']}]
if you're wanting to resolve to an object you should give the item a key, for instance, I've used the index in this example
let combinedScenesObj = _.reduce(sections, (prev, section, index) => {
return {...prev, [index]:[...section.guideScenes]};
}, {})
// { 0: ["hello"], 1: ["world"], 2: ["hi", "yo"] }

Related

TypeError: Cannot read properties of undefined (reading 'products')

I am trying to get products, then check if the product pId is in an array, and filter if it is.
I get an error when i soft refresh of 'TypeError: Cannot read properties of undefined' (reading 'products'), almost like my 'this.products' isnt populated yet when computed is trying to get the data. Tried adding some if statements to check data is there but no luck.
<script>
export default {
data() {
return {
popular_products: [],
products: [],
}
},
computed: {
bestsellers() {
const keywords = this.popular_products
let array = []
for (var index = 0; index < keywords.length; index++) {
const keyword = this.products.data.products.product.filter(
(product) => product.pId == keywords[index].ProductNumber
)
array = array.concat(keyword)
}
return array
},
},
mounted() {
axios
.get(
'https://myurl/admin/api/collections/get/popularproducts?token=account-9306f9192049d3c442e565f2de5372'
)
.then((response) => (this.popular_products = response.data.entries))
axios
.get('https://myurl/products.json')
.then((response) => (this.products = response))
},
}
</script>
The problem is with this line:
let keyword = this.products.data.products.product.filter(product => product.pId == keywords[index].ProductNumber);
more specific with this read: data.products.
You see, computed property bestsellers is evaluated before your axios calls are finished.
Because of that, Vue can't find products in data because your this.products doesn't have data key.
The best solution would be to change this assignment:
- .then(response => (this.products = response)); // delete this line
+ .then(response => (this.products = response.data.products)); // add this line
Update After comment.
if (this.products.product) {
return this.products.product.filter(...)
} else {
return []
}

How to get array of specific attributes values in cypress

I have a few elements in DOM and each of them has its own attribute 'id'. I need to create a function which iterates throw all of these elements and pushes values into the array. And the happy end of this story will be when this function will give me this array with all 'id' values.
I have tried this:
function getModelIds() {
let idList = [];
let modelId;
cy.get(someSelector).each(($el) => {
cy.wrap($el).invoke('attr', 'id').then(lid => {
modelId = lid;
idList.push(modelId);
});
});
return idList;
}
Will be very appreciated if you help me with rewriting this code into a function which will return an array with all 'id' values.
You can have a custom command:
Cypress.Commands.add(
'getAttributes',
{
prevSubject: true,
},
(subject, attr) => {
const attrList = [];
cy.wrap(subject).each($el => {
cy.wrap($el)
.invoke('attr', attr)
.then(lid => {
attrList.push(lid);
});
});
return cy.wrap(attrList);
}
);
You can use it later like this:
cy.get(someSelector)
.getAttributes('id')
.then(ids => {
cy.log(ids); // logs an array of strings that represent ids
});

Array of unique objects in react native

There is a method to store an array of objects for question id as key and answer as a value.
But it is creating multiple objects of same question id because of saving the previous state of object.
handleSelect(questionId,index,value){
this.setState((oldState)=>({selectedOptions:[...oldState.selectedOptions,{question:questionId,answer:value}]}))
}
How can i create unique array of objects?
You can achieve this in a number of ways - one way might be to use the Array#reduce() method like so:
handleSelect(questionId,index,value) {
// Your array containing items with duplicate questions
const arrayWithDuplicates = [
...this.state.selectedOptions,
{ question:questionId,answer:value }
];
// Use reduce to create an array of items with unique questions
const selectedOptions = arrayWithDuplicates
.reduce((uniqueArray, item) => {
const isItemInUniqueArray = uniqueArray.find(uniqueItem => {
return uniqueItem.question === item.question;
});
if(!isItemInUniqueArray) {
uniqueArray.push(item);
}
return uniqueArray
}, [])
this.setState((oldState)=>({selectedOptions: selectedOptions}))
}
This thread is closed now. I got the solution for my problem.
handleSelect(questionId,index,value) {
let question = this.state.selectedOptions.find((questions) => {
return questions.question === questionId
});
if(question){
this.setState(prevState => ({
selectedOptions: prevState.selectedOptions.map(
obj => (obj.question === questionId ? Object.assign(obj, { answer: value }) : obj)
)
}));
}
else{
this.setState((oldState)=>({selectedOptions:[...oldState.selectedOptions,{question:questionId,answer:value}]}))
}
}

FlatList single select cell

I followed the example from official docs, here is how to implement multiselection feature:
state = { selected: (new Map(): Map<string, boolean>) };
onPressItem = (id) => {
this.setState((state) => {
const selected = new Map(state.selected);
selected.set(id, !selected.get(id));
return { selected };
});
};
I'm struggling with making it single select though. It's easy to return new Map with false values anytime cell is tapped, but that means the cell cannot be deselected by another tap on it, which is the desired feature in my case.
onPressItem = (id) => {
this.setState((state) => {
const selected = new Map();
selected.set(id, !selected.get(id));
return { selected };
});
};
How would you implement it? Should I use lodash to iterate over the Map to find the one that already is true and change its value (now sure how to iterate over Map though), or maybe there is some better approach I am missing right now?
EDIT
Iterating over elements of the selected Map seems to be a really ugly idea, but it is simple and it actually works. Is there any better way to do it that I am missing out on?
onPressItem = (id: string) => {
this.setState((state) => {
const selected = new Map(state.selected);
selected.set(id, !selected.get(id));
for (const key of selected.keys()) {
if (key !== id) {
selected.set(key, false);
}
}
return { selected };
});
};
Thanks in advance
You can just set only one value instead of a map like this
onPressItem = (id) => {
this.setState((state) => {
const selected = selected === id ? null : id;
return { selected };
});
};
I had the same issue, my solution was:
_onPressItem = (id: string) => {
// updater functions are preferred for transactional updates
this.setState((state) => {
// copy the map rather than modifying state.
const selected = new Map(state.selected);
// save selected value
let isSelected = selected.get(id);
// reset all to false
selected.forEach((value, key) => {
selected.set(key, false);
});
// then only activate the selected
selected.set(id, !isSelected);
return { selected };
});
};

Iterating with v-for on dynamic item

I'm trying to iterate through a db object I fetch during created(), I get the values in a console.log but the v-for template part remains empty. My sub-question is : is this good practice ? I'm quite new to Vue and my searches on this issue make me think it's a lifecycle issue.
Thanks for the help.
TEMPLATE PART :
.content(v-for="(content, key, index) in contents")
h3 {{key}}
.line
| {{getValue(content)}} // this is blank
METHODS PART:
getValue(value) {
PagesService.fetchDataWrap({
id: value
}).then((response) => {
const test = response.data.values[0].value
console.log(test) //this is working and gives the right value
return test
})
},
getPage() {
PagesService.fetchPage({
id: this.$route.params.page
}).then((response) => {
this.name = response.data.result.name
this.langs = response.data.result.langs
this.project = response.data.result.parent
this.contents = response.data.result.values
})
this.getProject()
}
console.log(this.contents) result :
{__ob__: Observer}
footer: "5a29943b719236225dce6191"
header: "5a29a9f080568b2484b31ee1"
which is the values I want to send when v-for iterates on contents so the getValue can process it to fetch corresponding values
I wouldn't recommend attempting to output the value of an asynchronous method. It's highly unlikely that it will work correctly.
Instead, populate your contents array / object fully during the created hook. For example, this can replace the contents hash value with whatever comes back from fetchDataWrap...
getPage () {
PagesService.fetchPage({
id: this.$route.params.page
}).then(response => {
this.name = response.data.result.name
this.langs = response.data.result.langs
this.project = response.data.result.parent
let contents = response.data.result.values
Promise.all(Object.keys(contents).map(key => {
// map each key to a "fetchDataWrap" promise
return PageService.fetchDataWrap({
id: contents[key]
}).then(res => {
// replace the hash with the resolved value
contents[key] = res.data.values[0].value
})
}).then(() => {
// all done, assign the data property
this.contents = contents
})
})
}
Then you can trust that the content has been loaded for rendering
.content(v-for="(content, key, index) in contents")
h3 {{key}}
.line
| {{content}}