mapping one object to another using some functional programming - javascript

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);

Related

how to get array according to conditions in javascript

My array comes like this
var data=[{PRODUCT : P1}, {PRODUCT: P2}]
I wantt to convert this into [P1, P2].
Sometimes my array comes like this
var data=[{ITEM: I1, QUANTITY:1}, {ITEM: I2, QUANTITY:2}]
I wantt to convert this into [I1, I2].
so can we make a common function, where I just want to extract particular value of array and make a new array.
p.s. Thank you in advance
I tried to write the logic like this:
data.map((d, index) => { var result= [];
result.includes(d[0]); })
but it,s not dynamic
You could define a function which will always get the first value of the first object key, this should satisfy your needs based on the above
var data1 = [{
ITEM: 'I1',
QUANTITY: 1
}, {
ITEM: 'I2',
QUANTITY: 2
}]
var data2 = [{
PRODUCT: 'P1'
}, {
PRODUCT: ' P2'
}]
function getArrayOfValues(list) {
return list.reduce((acc, x) => {
const firstValue = Object.values(x)[0];
acc.push(firstValue)
return acc;
}, [])
}
const result1 = getArrayOfValues(data1)
console.log(result1)
const result2 = getArrayOfValues(data2)
console.log(result2)
function getProductOrItem(list) {
return list.reduce((accumulator, obj) => {
if (obj.PRODUCT) {
accumulator.push(obj.PRODUCT);
} else if (obj.ITEM) {
accumulator.push(obj.ITEM);
}
return accumulator;
}, [])
}
you can iterate through your array with map() method and inside it extract the value of a first entity of an object in your array and simply get a new array with all values:
const data1 =[{PRODUCT : 'P1'}, {PRODUCT: 'P2'}]
const data2 = [{ITEM: 'I1', QUANTITY: 1}, {ITEM: 'I2', QUANTITY: 2 }]
const transformValuesOfArray = (arrayToTransform) =>
arrayToTransform.map(value => {
const firstObjectValue = Object.values(value)[0]
return firstObjectValue
})
console.log(transformValuesOfArray(data1))
console.log(transformValuesOfArray(data2))

Alternative to Object.fromEntries?

I receive an object like this:
this.data = {
O: {
id: 0,
name: value1,
organization: organization1,
...,
},
1: {
id: 1,
name: value1,
organization: organization1,
...,
},
2: {
id: 2,
name: value2,
organization: organization2,
...,
},
...
}
I then filter by id and remove the Object which id matches the id I receive from the store like so:
filterOutDeleted(ids: any[], data: object,) {
const remainingItems = Object.fromEntries(Object.entries(data)
.filter(([, item]) => !ids.some(id => id === item.id)));
const rows = Object.keys(remainingItems).map((item) => remainingItems[item]);
return rows;
}
Unfortunately, I'm getting an error when building stating Property 'fromEntries' does not exist on type 'ObjectConstructor' and I am unable to make changes in the tsconfig file at this point. Is there an alternative for fromEntries for this case? Any help is much appreciated!
Create the object outside instead, and for every entry that passes the test, assign it to the object manually.
Also note that you can decrease the computational complexity by constructing a Set of the ids in advance:
const filterOutDeleted = (ids: any[], data: object) => {
const idsSet = new Set(ids);
const newObj = {};
for (const [key, val] of Object.entries(data)) {
if (!idsSet.has(val.id)) {
newObj[key] = val;
}
}
return newObj;
};

How do i setState with hooks of an object which has multiple arrays ? How to setdebtors here?

I am deleting an one id in an array, how do I setState after filtering it here?
https://codesandbox.io/s/react-example-1m2qn
const Debtors = () => {
const debtors = [
{
id: 1,
name: "John",
relation: "friend",
statement: [
{ id: 1, date: 2010, amount: "1000", purpose: "John" },
{ id: 2, date: 2014, amount: "2000", purpose: "john" }
]
},
,
{
id: 2,
name: "Jack",
relation: "Friend",
statement: [
{ id: 1, date: 2010, amount: "1000", purpose: "jack" },
{ id: 2, date: 2014, amount: "2000", purpose: "jack" }
]
}
];
const [newDebtors, setdebtors] = React.useState(debtors);
const handleDelete = (stat, i) => {
const newList = newDebtors[0].statement.filter(x => x.id !== stat.id);
// How to set debtors here ?
// setdebtors({ ...newDebtors, statement[0]: newList });
console.log(newList)
// How to set debtors here ?
There's two problems:
1) You are iterating off the original debtors object in your render, instead of the newDebtors state you created via useState(), which is why there does not appear to be any UI change.
You need: newDebtors[0].statement.map
2) You need to pass in the item index in your handleDelete() so it knows what item in the array to update. You can have the function do something like this:
In the onClick:
<a
href="javascript:;"
onClick={() => handleDelete(stat, i, 0)}
>
In the handleDelete():
const handleDelete = (stat, i, arrayIndex) => {
const updatedDebtors = newDebtors.map((item, index) => {
if (index === arrayIndex) {
return {
...item,
statement: item.statement.filter(
statement => statement.id !== stat.id
)
};
} else {
return item;
}
});
setDebtors(updatedDebtors);
};
See sandbox for full solution: https://codesandbox.io/s/react-example-x7uoh
You should do it like that:
setdebtors((prevState) => {
let newArray = Array.from(prevState); // Copy the array
// Manipulate the array as you wish
return newArray; // return it
});
The problem is you are mutating the array of "debtors" you need to map through the array of debtors and change any properties in the object.
const handleDelete = (stat, i) => {
const newList = newDebtors.map((debtor, i) => {
if (i === 0) {
debtor.statement = debtor.statement.filter(x => x.id !== stat.id);
}
return debtor;
});
setdebtors(newList);};
An even better approach is to use "useReducer" which is used for mutating more complex pieces of state, like you have here. THe docs are very helpful useReducer
Hmm I dont know what exactly you are trying to do,
Is this what you are looking for?
const handleDelete = (stat, i) => {
const newList = newDebtors[0].statement.filter(x => x.id !== stat.id);
const newFirstItem = {...newDebtors[0],statement: newList}
const newDebtorList = newDebtors.filter(x => x.id !== newFirstItem.id);
newDebtorList.unshift(newFirstItem);
setdebtors(newDebtorList);
}
I know this seems complex but you kinda actually need to do this as you cannot mutate an array in the state...
What I did here is I first created a new statement list(newList), then created a newFirstItem to be set as the new newDebtors[0], then created a new array(newDebtorList) of all the elements of newDebtors except the first one, I modified this array by pushing the newFirstItem to the 0th position(using unshift)
Finally updated the state with this new array...
hope it helps :)
Note: this is for changing the 0th element if you have the id please change the code accordingly

Compare two objects in JavaScript

I have constructed objects as follow:
Object 1
[ {
ext: '1287',
ip: '(Unspecified)',
queue: [ ]
} ]
Object 2
[ { Queue: '222',
Members:
[ {"ext":"1287"},
{"ext":"4118"} ],
Callers: [] },
{ Queue: '111',
Members:
[ {"ext":"4131"},
{"ext":"1287"},
{"ext":"4138"}
],
Callers: [] }]
I want to compare Object 1 and Object 2. If the value of ext key from Object 1 exists in the nested Members object of Object 2 then
the value of Queue should be pushed to a queue array and the final object should be like as shown below.
Final Object that I want
[{ ext: '1287',
ip: '(Unspecified)',
queue: [222, 111 ] }]
I need some hints regarding how a nested object like this is compared using lodash.
You can try following using Array.forEach and Array.some
let obj1 = [{ext: '1287',ip: '(Unspecified)',queue: []}];
let obj2 = [{Queue: '222',Members: [{"ext":"1287"},{"ext":"4118"}],Callers: []},{Queue: '111',Members: [{"ext":"4131"},{"ext":"1287"},{"ext":"4138"}],Callers: []}];
obj1.forEach(o => {
obj2.forEach(v => {
let exists = v.Members.some(m => m.ext === o.ext);
if (exists) o.queue.push(v.Queue);
});
});
console.log(obj1);
Improvement
You can improve the performance by first creating a map of obj1 with ext as key and object as value. Use Array.reduce and Object.assign
let obj1 = [{ext: '1287',ip: '(Unspecified)',queue: []}];
let obj2 = [{Queue: '222',Members: [{"ext":"1287"},{"ext":"4118"}],Callers: []},{Queue: '111',Members: [{"ext":"4131"},{"ext":"1287"},{"ext":"4138"}],Callers: []}];
let map = obj1.reduce((a, c) => Object.assign(a, {[c.ext] : c}), new Map());
obj2.forEach(v => {
v.Members.forEach(m => {
if(map[m.ext]) map[m.ext].queue.push(v.Queue);
});
});
console.log(obj1);
Solution without mutations:
const obj1 = [{ext: '1287',ip: '(Unspecified)',queue: []}];
const obj2 = [{Queue: '222',Members: [{"ext":"1287"},{"ext":"4118"}],Callers: []},{Queue: '111',Members: [{"ext":"4131"},{"ext":"1287"},{"ext":"4138"}],Callers: []}];
const hasExt = ext => o2 => o2.Members.some(m => m.ext === ext)
const result = obj1.map(o1 => {
const newQueue = obj2
.filter(hasExt(o1.ext))
.map(m => m.Queue);
return { ...o1, queue: [...o1.queue, ...newQueue] };
})
console.log(result);

How to use if statement within map in javascript/vuejs

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)

Categories

Resources