How to use if statement within map in javascript/vuejs - javascript

I'm building a small application in Vuejs where I'm getting a response data and I'm mapping it to a variable, I've got few elements which has empty array, so while mapping I want to check the condition and map accordingly. Here is my code:
this.model = a.map(i => Object.assign({
'id': i.id,
'meeting_date': i.schedule,
'meeting_call': i.type,
'event_type': i.event_type,
'venue': i.venue,
'with_client': i.with_client
},{
if(i.meeting.meeting_summaries)
{
'meeting_summaries': i.meeting_summaries.map(ms => ({
client_name: ms.client_name,
nature: ms.nature,
action: ms.action,
mention: ms.user_id,
feedback: ms.feedback
}))
}
},

map is purely functional, it doesn't modify the elements instead return a newly formed array, so you can do like this:
this.model = a.map(i => {
var item = {}
item['id']= i.id,
item['meeting_date']= i.schedule,
item['meeting_call']= i.type,
item['event_type']= i.event_type,
item['venue']= i.venue,
item['with_client']= i.with_client
if(i.meeting && i.meeting.meeting_summaries) {
item['meeting_summaries']= i.meeting.meeting_summaries.map(ms =>({
client_name: ms.client_name,
nature: ms.nature,
action: ms.action,
mention: ms.user_id,
feedback: ms.feedback
}))
}else {
item['meeting_summaries'] = []
}
return item
}

In your case you can just swap to ternary expression:
this.model = a.map(i => Object.assign({
'id': i.id,
'meeting_date': i.schedule,
'meeting_call': i.type,
'event_type': i.event_type,
'venue': i.venue,
'with_client': i.with_client
}, (i.meeting.meeting_summaries) ? { // if condition is met
'meeting_summaries': i.meeting_summaries.map(ms => ({
client_name: ms.client_name,
nature: ms.nature,
action: ms.action,
mention: ms.user_id,
feedback: ms.feedback
}))
} : {} // otherwise don't do anything
The idea is the following:
const a = [1, 2, 3, 4, 5];
const b = a.map(number => Object.assign({a: number},
(number > 2) ? {b: ++number} : {}, // add 'b' for numbers > 2
(number % 2 === 0) ? {c: number + ' is even'} : {} // add 'c' for even numbers
))
console.log(b)

Related

Convert string into an object using reduce javascript

I am trying to convert a string like "sheep" into an object like this:
{
"s":{
"initial":1,
"final":1
},
"h":{
"initial":1,
"final":1
},
"e":{
"initial":2,
"final":2
},
"p":{
"initial":1,
"final":1
}
}
Currently I can use reduce method in javascript and achive this:
const names = 'sheep'.split('');
const count = (names) =>
names.reduce((acc, name) => ({ ...acc, [name]: (acc[name] || 0) + 1 }), {});
console.log(count(names)) //{ s: 1, h: 1, e: 2, p: 1 }
I have tried to read similar posts but I am pretty new to JS. Can anyone please help me? Thanks.
Try like this
const names = "sheep".split("");
const count = (names) =>
names.reduce(
(acc, name) => ({
...acc,
[name]: {
initial: (acc?.[name]?.initial ?? 0) + 1,
final: (acc?.[name]?.final ?? 0) + 1,
},
}),
{}
);
console.log(count(names));
This works printing your desired output but I'm not sure why you want the initial and final counts to be the same
const letters = 'sheep'.split('');
const count = (letters) =>
letters.reduce((obj, letter) => ({ ...obj, [letter]: {initial: (obj[letter]?.initial || 0) + 1, final: (obj[letter]?.final || 0) + 1} }), {});
console.log(count(letters))
Alternatively, you could expand a function there using braces and do some if's in order to have the code more readable instead of the ternaries

How to change a nested object value of a redux state

I have the following state
const state = {
courses: [],
series: [],
course: {
title: 'testing',
course_notes: [
{
id: 1,
note: "one" // want to edit this
},
{
id: 2,
note: "two"
}
]
}
}
I want to change state.course.course_notesp[0].name
I've never fully understood how this works, read a lot of tutorials, I feel I know how it works but it always trips me up. This is what I am trying
const m = {
...state,
course: {
course_notes:[
...state.course.course_notes,
state.course.course_notes.find(n => n.id === 1).note = "edited"
]
}
}
That seems to add edited as an extra node. state.course.course_notes.length ends up being 3.
You are using the spread operator for arrays like you would for objects.
Assume you have an object
const obj = { a: 1, b: 2 }
If you say:
{...obj, a: 2}
What you are saying is:
{ a: 1, b: 2, a: 2 }
The property a is defined twice, but the second one overrrides the first one.
If you do something similar for an array, however, the result would be different:
const arr = [1, 2];
const newArr = [...arr, arr[0]];
// here the result would be [1, 2, 1]
This is why when you are saying:
course_notes:[
...state.course.course_notes,
state.course.course_notes.find(n => n.id === 1).note = "edited"
]
what it does is add an extra element to the array.
What you should do is instead create a modified version of the array, for example using map
course_notes: state.course.course_notes.map(el => {
if (el.id === 1) {
el.note = 'edited';
}
return el;
});
There are lots of ways you could modify the state of your store to update one element of course_notes.
If we assumed the ids to be unique, I would map the previous array modifying the element with id 1.
....
course_notes: state.course.course_notes.map(x => x === 1
? { ...x, note: 'edited' }
: x
)
...

RxJs: Paginate through API recursively and find value from list

I am using rxjs v6.4.0. I am trying to paginate through an API searching for a very specific channel where name equals "development". I am using expand to recursively call the API and get new pages. The end result gives me a concatenated list of channels. Then I filter out all channels where name not equal to "development". However I am getting an error: TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
const Rx = require('rxjs')
const Rx2 = require('rxjs/operators')
const getChannel = (cursor) => {
return this.service.getData(`${url}?cursor=${cursor || ''}`)
.pipe(Rx2.map(resp => JSON.parse(resp.body)))
.pipe(Rx2.expand(body => { // recurse the api until no more cursors
return body.response_metadata &&
body.response_metadata.next_cursor ?
getChannel(body.response_metadata.next_cursor) : Rx.EMPTY
}))
.pipe(Rx2.pluck('channels'))
.pipe(Rx2.mergeAll()) // flattens array
.pipe(Rx2.filter(c => {
console.log('finding', c.name === 'development')
return c.name === 'development'
}))
}
The find callback should return a boolean, not an Observable. E.g.
find(c => c.name === 'development')
UPDATE
Heres a modified example of yours. I've removed generators as they are more complicated then our case needs.
const { of, EMPTY, throwError } = rxjs;
const { filter, tap, expand, pluck, mergeAll } = rxjs.operators;
const DATA =
[ {channels: [{id: 123, name: 'test'}, {id:4, name: 'hello'}], cursor: 1}
, {channels:[{id: 1, name: 'world'}, {id: 2, name: 'knows'}], cursor: 2}
, {channels:[{id: 3, name: 'react'}, {id: 5, name: 'devcap'}], cursor: false}
];
function getChannel(){
return getBlock()
.pipe(
expand(x => x.cursor ? getBlock(x.cursor) : EMPTY),
pluck('channels'),
mergeAll(),
filter(c => c.name === 'devcap')
)
}
getChannel().subscribe({
next: console.log,
error: console.error
});
function getBlock(index = 0) {
if (index >= DATA.length){
throwError('Out of bounds');
}
return of(DATA[index]);
}
UPDATE 2
Your solution didn't work due to recursion being done through solely getChannel(). When you do the expand -- you run another cycle through getChannel(). Meaning that you run pluck-mergeAll-filter chain twice on each recursively fetched value! Plucking and flattering it twice gives you undefined -- therefore the error.
In your playground -- try separating out this code
let getValue = ()=>{
const next = gen.next();
if (!next || !next.value) { return EMPTY; }
return next.value;
}
and use it in the expand, like this:
let getChannel = () => {
return getValue()
.pipe(
expand(body => {
return body.cursor ? getValue() : EMPTY
}),
pluck('channels'),
mergeAll(),
filter(c => c.name === 'devcap'),
)
}
Let me know if this is the functionality you are looking for https://codepen.io/jeremytru91/pen/wOQxbZ?editors=1111
const {
of,
EMPTY
} = rxjs;
const {
filter,
tap,
expand,
take,
pluck,
concatAll,
flatMap,
first
} = rxjs.operators;
function* apiResponses() {
yield of({channels: [{id: 123, name: 'test'}, {id:4, name: 'hello'}], cursor: 1});
yield of({channels:[{id: 3, name: 'react'}, {id: 5, name: 'devcap'}], cursor:3});
yield of({channels:[{id: 1, name: 'world'}, {id: 2, name: 'knows'}], cursor: 2});
yield of({channels:[{id: 4, name: 'react'}, {id: 6, name: 'devcap'}], cursor:4});
}
let gen = apiResponses();
function getChannel() {
return gen.next().value // simulate api call
.pipe(
expand(body => {
return body.cursor ? gen.next().value : EMPTY
}),
pluck('channels'),
flatMap(channels => {
const filtered = channels.filter(channel => channel.name === 'devcap')
if(filtered.length) {
return filtered;
}
return EMPTY;
}),
first()
);
}
getChannel().subscribe(data => {
console.log('DATA!! ', data)
}, e => {
console.log('ERROR', e)
throw e
})

Copy and update nested object before setstate. react-native

I have nested objects on my state:
constructor(props) {
super(props);
this.state = {
aba1Array: [
{
item:[
{
id: 1,
nome: "apple",
selected:false,
},
{
id: 2,
nome: "juice",
selected:false,
}
]
},
{
item:[
{
id: 3,
nome: "apple",
selected:false,
},
{
id: 4,
nome: "juice",
selected:false,
}
]
}
],
};
}
Now I want to make a copy of the array, update an item, and setState again (is this the correct way to update, right ?), how to do this ? this is my current code:
updateItem(id){
let newItems = Object.keys(this.state.aba1Array).map((subitem) => {
subitem.item.map((item) => item.id===id ? {...item, selected: true } : item);
});
this.setState({ aba1Array: newItems });
}
this is returning:
TypeError: undefined is not an object (evaluating 'subitem.item.map')
edited: The problem begun when I added second level of nesting , before it was working fine with just one level , like this :
let newItems = Object.keys(this.state.aba1Array).map((item) => {
item.id===id ? {...item, selected: true } : item);
edited2: in another part of the screen where I only list the items , i'm using:
listItem() {
return this.state.aba1Array.map((subitem)=> {
return ( subitem.item.map((item) => { return ( <View>
...
and it works fine. Why in the update function it gives me error ? I don't understand
Edited3: - SOLUTION -
My solution was to use library object-path-immutable:
updateItem(itemIndex,subIndex){
const newObj = immutable.update(this.state.aba1Array, [subIndex,'item', itemIndex , 'selected' ], v => true );
this.setState({ aba1Array: newObj });
}
My solution was to use library object-path-immutable:
updateItem(itemIndex,subIndex){
const newObj = immutable.update(this.state.aba1Array, [subIndex,'item', itemIndex , 'selected' ], v => true );
this.setState({ aba1Array: newObj });
}
Best solution :
I forgot to return the value on map function , so the new array wasnt getting the full tree of the object , the best solution is:
let newItems = this.state.aba1Array.map((subitem) => {
subitem.item.map((item) => {
item.selected = true;
return item;
})
return subitem;
});

mapping one object to another using some functional programming

I would like to map one array of object into another in a more functional style, I am using typescript.
Basically I am using delete to remove a property on a object, I would like to know if there is a better way to write it.
const data = props.data.map(d => ({
order: d.position,
logs: d.batches.map(b => {
let log= {
amount: b.scrap,
batchNumber: '', // NO GOOD
}
if (!b.batch || b.batch.length === 0) {
delete log.batchNumber // NO GOOD
}
return log
}),
}))
example input data:
const data = [
position: 1,
batches: [
{batchNumber: '', ammount: 3}
]
]
result:
const data = [{
order: 1,
logs:[ {ammount:3}]
}
]
You can do another map on the batches to return a new array of objects, and attach that to your returned object instead:
const out = data.map(({ position: order, batches }) => {
const logs = batches.map(({ batchNumber, ammount }) => {
if (batchNumber) return { batchNumber, ammount };
return { ammount };
});
return { order, logs }
});
DEMO
One approach would be to make a shallow copy of the target omitting keys you want to delete, for example:
let drop = key => obj => Object.keys(obj).reduce((r, k) =>
k === key ? r : {...r, [k]: obj[k]}, {});
let test = [
{foo:11, bar:2, baz: 3},
{foo:22, bar:2, baz: 3},
{foo:33, bar:2, baz: 3},
];
console.log(test.map(drop('bar')));
To add another option to the mix: it is possible to use Object.assign to optionally assign the property:
const data = [{
position: 1,
batches: [{batchNumber: '',ammount: 3}, {batchNumber: 'withNr',ammount: 4}]
}];
const res = data.map(d =>
({
order: d.position,
logs : d.batches.map(({ammount, batchNumber}) => Object.assign({ammount}, batchNumber ? {batchNumber} : null ))
})
);
console.log(res);

Categories

Resources