Retrieving item between two arrays of object with complex conditions - javascript

I got two lists of objects :
let list1 = [{id: '1', status: 'use', comment: 'xxxx'}, {id: '2', status: 'ready', comment: 'yyyy'}, {id: '3', status: 'ready', comment: 'zzzz'}];
let list2 = [{uid: '1', elec: 60}, {uid: '2', elec: 60}, {uid: '10', elec: 60}, {uid: '3', elec: 40}];
What i want is to retrieve an object of list2 that have elec > 50 and the same uid than one item id of the list1 only if the item of the list1 have a status == "ready". Also, i want to add to this item the parameter 'comment' from the object of the list1.
In this exemple, my result value would be : {uid: '2', elect: 60, comment: 'yyyy'}.
I did this :
let list1Filtered = list1.filter(itemList1 => itemList1.status == 'ready');
let list2Filtered = list2.filter(itemList2 => itemList2.elec > 50);
var result;
for ( let itemList1Filtered of list1Filtered ) {
for ( let itemList2Filtered of list2Filtered ) {
if (!result && itemList1Filtered.id == itemList2Filtered.uid) {
result = itemList2Filtered;
result.comment = itemList1Filtered.comment;
}
}
}
return result;
I want to know if there is a more elegant and/or more sophisticated way to do this in Javascript.

You could collect wanted comments from list1 and reduce list2 with a check for the value of elec and if an item exist from the other list. Then return a new object.
This approach needs only two loops.
const
list1 = [{ id: '1', status: 'use', comment: 'xxxx' }, { id: '2', status: 'ready', comment: 'yyyy' }, { id: '3', status: 'ready', comment: 'zzzz' }],
list2 = [{ uid: '1', elec: 60 }, { uid: '2', elec: 60 }, { uid: '10', elec: 60 }, { uid: '3', elec: 40 }],
l1 = list1.reduce((r, { id, status, comment }) => {
if (status === 'ready') r[id] = { comment };
return r;
}, {}),
result = list2.reduce((r, o) => {
if (o.elec > 50 && o.uid in l1) r.push({ ...o, ...l1[o.uid]})
return r;
}, []);
console.log(result);

let result = {
...list2.filter(
a => a.elec > 50 && a.uid === list1.filter(b => b.status === "ready")[0].id)[0],
comments: list1.filter(b => b.status === "ready")[0].comment
}

Try this:
let list1 = [
{ id: '1', status: 'use', comment: 'xxxx' },
{ id: '2', status: 'ready', comment: 'yyyy' },
{ id: '3', status: 'ready', comment: 'zzzz' },
];
let list2 = [
{ uid: '1', elec: 60 },
{ uid: '2', elec: 60 },
{ uid: '10', elec: 60 },
{ uid: '3', elec: 40 },
];
let result = null;
let itemFound;
const filteredList = list2.filter((list2Item) => {
if (!result) {
itemFound =
list2Item.elec > 50 &&
list1.find(
(list1Item) =>
list2Item.uid === list1Item.id && list1Item.status === 'ready'
);
if (itemFound) {
result = {
uid: list2Item.uid,
elect: list2Item.elec,
comment: itemFound.comment,
};
}
}
});
console.log(result);

This should do the job:
const list1 = [{
id: '1',
status: 'use',
comment: 'xxxx'
}, {
id: '2',
status: 'ready',
comment: 'yyyy'
}, {
id: '3',
status: 'ready',
comment: 'zzzz'
}];
const list2 = [{
uid: '1',
elec: 60
}, {
uid: '2',
elec: 60
}, {
uid: '10',
elec: 60
}, {
uid: '3',
elec: 40
}];
const filteredList = list2.filter(item => {
const readyItemsList1 = list1.filter(item => item.status === 'ready').map(item => item.id);
return item.elec > 50 && readyItemsList1.indexOf(item.uid) > -1
}).map(item => {
const comment = list1.find(it => it.id === item.uid).comment;
item.comment = comment;
return item;
});
console.log(filteredList);

you can restructure your data. List1 convert it in object. And now we can find solution in O(n).
let list1 = [{
id: '1',
status: 'use',
comment: 'xxxx'
}, {
id: '2',
status: 'ready',
comment: 'yyyy'
}, {
id: '3',
status: 'ready',
comment: 'zzzz'
}];
let list2 = [{
uid: '1',
elec: 60
}, {
uid: '2',
elec: 60
}, {
uid: '10',
elec: 60
}, {
uid: '3',
elec: 40
}];
const itemList = {}
list1.forEach(item => {
if (item.status === 'ready') {
itemList[item.id] = item.comment
}
});
const result = list2.filter(item => itemList[item.uid] && item.elec > 50).map(item => {
item['comment'] = itemList[item.uid]
return item
})
console.log(result)

filter and map will help you.
filter can select items fit some criteria, and map let you change the data to pick.
For list1, select item with status equals 'ready', then take only id.
var ready_id_array = list1.filter(item=>item.status == 'ready').map(item=>item.id);
For list2, check item that its uid contained in ready_id_array, and elec larger than 50.
var result = list2 .filter(item => ready_id_array.indexOf(item.uid) > -1 && item.elec > 50);
to append the comment, a dictionary is created and then put comment back to result
var comment_dictionary = list1.reduce((a,x) => ({...a, [x.id]: x.comment}), {});
result.forEach(item => item.comment = comment_dictionary[item.uid]);
and you will have the result.
[{uid: "2", elec: 60, comment: "yyyy"}]

Related

Merge all keys into single object with the same Id in JavaScript

I have an array of objects Like that.
var orderResults = [{
id: '1',
name: 'Maya Mahardhani',
payment_amount : 100,
sku: 'ST001802027',
seq: '1'
},
{
id: '1',
name: 'Maya Mahardhani',
payment_amount : 50,
sku: 'ST000703044',
seq: '2'
},
{
id: '2',
name: 'Tara Debu Batara',
payment_amount : 100,
sku: 'ST005101001',
seq: '1'
},
{
id: '3',
name: 'Nikita Gigir',
payment_amount : 100,
sku: 'ST004403030',
seq: '1'
}]
But I am trying to extract the data in the following way.
[{
id: '1',
name: 'Maya Mahardhani',
total_amount : 150,
sku_1: 'ST001802027',
sku_2: 'ST000703044',
},
{
id: '2',
name: 'Tara Debu Batara',
total_amount : 100,
sku_1: 'ST005101001'
},
{
id: '3',
name: 'Nikita Gigir',
total_amount : 100,
sku_1: 'ST004403030'
}]
I give try with the reduce function of JavaScript. But it overwrites the prev key with the old one. My Code Snippet is like that. I think I am closer to solve. But still seeking the help
orderResults.reduce((res, obj) => {
res[obj.id] = {
total_amount : (obj.id in res ? res[obj.id].total_amount : 0) + obj.payment_amount,
name : obj.name,
}
res[obj.id]['sku' + obj.seq]= obj.sku
return res;
},[])
Thank you
Do not create a new object every time.
const result = Object.values(orderResults.reduce((res, obj) => {
res[obj.id] = res[obj.id] ||
{
id: obj.id,
total_amount : 0,
name : obj.name,
};
res[obj.id].total_amount += obj.total_amount;
res[obj.id]['sku' + obj.seq] = obj.sku;
return res;
},[]))
I think this will do what you are trying to do:
orderResults.reduce((res, obj, i) => {
const existingIdx = res.findIndex(r => r.id === obj.id)
if (existingIdx > -1) {
res[existingIdx] = {
...res[existingIdx],
total_amount : res[existingIdx].total_amount ? res[existingIdx].total_amount + obj.payment_amount : res[existingIdx].payment_amount + obj.payment_amount,
name : obj.name,
['sku_' + obj.seq]: obj.sku
}
} else {
res.push({
id: obj.id,
name: obj.name,
total_amount: obj.payment_amount,
['sku_' + obj.seq]: obj.sku
})
}
return res;
},[])
Notice this line will preserve the original object and overwrite any duplicate keys that are defined afterwards:
...res[existingIdx],
Also when your code runs this line:
res[obj.id] = {
it is setting a specific index in the array, which I don't think you want to do. You want to either push (if the object id hasn't been added yet), or overwrite the existing object at the original insertion point when the object with the same id was created.
[{
id: '1',
name: 'Maya Mahardhani',
payment_amount: 100,
sku: 'ST001802027',
seq: '1'
},
{
id: '1',
name: 'Maya Mahardhani',
payment_amount: 50,
sku: 'ST000703044',
seq: '2'
},
{
id: '2',
name: 'Tara Debu Batara',
payment_amount: 100,
sku: 'ST005101001',
seq: '1'
},
{
id: '3',
name: 'Nikita Gigir',
payment_amount: 100,
sku: 'ST004403030',
seq: '1'
}].reduce((acc, current) => {
const { id, name, payment_amount, sku, seq } = current;
const previousRecord = acc[id];
if (typeof previousRecord === 'object') {
return {
...acc,
[id]: {
...previousRecord,
[`sku_${seq}`]: sku,
total_amount: previousRecord.total_amount + payment_amount
}
}
} else {
return {
...acc,
[id]: {
id,
name,
[`sku_${seq}`]: sku,
total_amount: payment_amount
}
}
}
}, {}) // returns an Object; use Object.values to convert to a list

How to retrieve a particular property from array of objects based on multiple conditions using react and javascript?

i have an array of objects like below,
const arr_obj = [
{
id: '1',
jobs: [
{
completed: false,
id: '11',
run: {
id: '6',
type: 'type1',
},
},
{
completed: true,
id: '14',
run: {
id: '17',
type: 'type1',
},
},
{
completed: false,
id: '12',
run: {
id: '7',
type: 'type2',
},
},
],
},
{
id: '2',
jobs: [
{
completed: true,
id: '13',
run: {
id: '8',
type: 'type2',
},
},
{
completed: true,
id: '16',
run: {
id: '9',
type: 'type1',
},
},
{
completed: true,
id: '61',
run: {
id: '19',
type: 'type1',
},
},
],
},
{
id: '3',
jobs: [
{
completed: false,
id: '111',
run: {
id: '62',
type: 'type1',
},
},
],
},
],
and an arr_ids = ["1","2"]
now i have to filter those ids from arr_obj matchings arr_ids which i do like so
const filteredIds = arr_obj.filter(obj => arr_ids.includes(obj.id));
so the filtered_arrobj = [
{
id: '1',
jobs: [
{
completed: false,
id: '11',
run: {
id: '6',
type: 'type1',
},
},
{
completed: true,
id: '14',
run: {
id: '17',
type: 'type1',
},
},
{
completed: false,
id: '12',
run: {
id: '7',
type: 'type2',
},
},
],
},
{
id: '2',
jobs: [
{
completed: true,
id: '13',
run: {
id: '8',
type: 'type2',
},
},
{
completed: true,
id: '16',
run: {
id: '9',
type: 'type1',
},
},
{
completed: true,
id: '61',
run: {
id: '19',
type: 'type1',
},
},
],
},
]
Now i will have to get the ids from filtered_arrobj whose runs are of type "type1" and none of the jobs have completed: false.
so the expected output from filtered_arrobj is "2"
here in the above example id "1" is not taken because id "1" has job completed: true but it also has job completed: false for type "type1".
what i have tried?
const output = filtered_arrobj.map(obj => obj.jobs.map(job => job.run.type ===
"type1" && job.completed === true));
when i log the output its gives like so
[
[false,true,false],
[true,true,true]
]
this not giving me the id of the obj whose job has run of type = "type1" and which has no jobs completed: false.
how can i get that. could someone help me with this. I am new to programming and learning on the go. thanks.
Use Array.filter() on arrObj filtered by arrIds which you already did, after that filter for jobs array (inside above filter) which contains type as type1 and then check if filtered jobs has completed as true then return true otherwise false.
const arrObj = [
{
id: '1',
jobs: [
{
completed: false,
id: '11',
run: {
id: '6',
type: 'type1',
},
},
{
completed: true,
id: '14',
run: {
id: '17',
type: 'type1',
},
},
{
completed: false,
id: '12',
run: {
id: '7',
type: 'type2',
},
},
],
},
{
id: '2',
jobs: [
{
completed: true,
id: '13',
run: {
id: '8',
type: 'type2',
},
},
{
completed: true,
id: '16',
run: {
id: '9',
type: 'type1',
},
},
{
completed: true,
id: '61',
run: {
id: '19',
type: 'type1',
},
},
],
},
{
id: '3',
jobs: [
{
completed: false,
id: '111',
run: {
id: '62',
type: 'type1',
},
},
],
},
];
const arrIds = ["1", "2"];
const filteredIds = arrObj.filter(obj => {
if (arrIds.includes(obj.id)) {
const jobs = obj.jobs.filter(job => job.run.type === "type1");
if (jobs.length > 0) {
return jobs.every(job => job.completed === true);
}
return false;
}
return false;
});
console.log(filteredIds);
This example iterates over the data array with a simple for...of loop and uses every to check the required conditions.
const data=[{id:"1",jobs:[{completed:false,id:"11",run:{id:"6",type:"type1"}},{completed:true,id:"14",run:{id:"17",type:"type1"}},{completed:false,id:"12",run:{id:"7",type:"type2"}}]},{id:"2",jobs:[{completed:true,id:"13",run:{id:"8",type:"type2"}},{completed:true,id:"16",run:{id:"9",type:"type1"}},{completed:true,id:"61",run:{id:"19",type:"type1"}}]}];
let out = [];
for (let obj of data) {
// Find out if the jobs of type1 have all completed...
const runCompleted = obj.jobs.every(job => {
return job.completed && job.run.type === 'type1';
});
// If not add the id to the output array
if (!runCompleted) out.push(obj.id);
}
// And then you just check the length of the array
console.log(out);
console.log(out.length);
I would do it in one iteration, something like this:
const output = arr_obj.filter(obj => {
let hasType1 = false;
return (
arr_ids.includes(obj.id) &&
obj.jobs.every(job => {
if (job.run.type === 'type1') hasType1 = true;
return job.completed === true;
}) &&
hasType1
);
});
Or if you only need to check for completeness on type1 jobs:
const output = arr_obj.filter(obj => {
let hasType1 = false;
return (
arr_ids.includes(obj.id) &&
obj.jobs.every(job => {
if (job.run.type === 'type1') {
hasType1 = true;
return job.completed === true;
}
return true;
}) &&
hasType1
);
});
This should work :
const arr = arr_obj.reduce((accumulator, current) => {
const doSatisfyCondition = arr_ids.includes(current.id) && current.jobs.every(item => item.completed) && current.jobs.some(item => item.run.type === "type1")
if (doSatisfyCondition) accumulator.push(current.id);
return accumulator;
}, [])
console.log(arr)```
This code solves the problem, it is a bit focused on optimization but it works.
const arr=[{id:"1",jobs:[{completed:!1,id:"11",run:{id:"6",type:"type1"}},{completed:!0,id:"14",run:{id:"17",type:"type1"}},{completed:!1,id:"12",run:{id:"7",type:"type2"}}]},{id:"2",jobs:[{completed:!0,id:"13",run:{id:"8",type:"type2"}},{completed:!0,id:"16",run:{id:"9",type:"type1"}},{completed:!0,id:"61",run:{id:"19",type:"type1"}}]},{id:"3",jobs:[{completed:!1,id:"111",run:{id:"62",type:"type1"}}]}];
// For optimitation
const targets = ["1", "2"].reduce((a, c) => ({...a, [c]: true}), {})
const result = arr.filter(data => {
if(targets[data.id]) {
let containType1 = false
const jobs = data.jobs
const canSave = jobs.every(job => {
const isType1 = job.run.type === "type1"
containType1 = isType1 ? true : containType1
return isType1 ? job.completed : true
})
return canSave && containType1
}
return false
})
console.log(result)
Does this do the job?
const ids = new Set(arr_ids);
const filtered_objects = arr_obj.filter(el => ids.has(el.id));
The Set constructor creates a Set of Ids insread of an array. This enables you to use the has-function to check if the id property of the element coming from the arr_obj array is in the set "ids".
If you are new to programming you could check in to the Set and Map data structure and its related functions. There are some really good ways to filter and merge data if you can create some kind of uniqueness for every element in the array that you want to create a set from. In this example the Id is a good unique value.

Compare two arrays and filter with condition

I have to filter an array and compare it with another array with a condition.
const array1 = [
{id: 'q1', type: 'single'},
{id: 'q2', type: 'multiple'},
{id: 'q3', type: 'single'},
{id: 'q4', type: 'single'}
];
const array2 = [
{newId: 'q1', status: 'submitted'},
{newId: 'q2', status: 'drafted'},
{newId: 'q2', status: 'submitted'},
{newId: 'q2', status: 'submitted'},
{newId: 'q4', status: 'drafted'}
];
const resultArray = [
{id: 'q2', type: 'multiple'},
{id: 'q3', type: 'single'}
];
I have tried with the map function but I get the wrong result. This is my code:
let resultArray = [];
map(array1, el => {
if(el.type==='single'){
map(array2, elm => {
if(el.id!==elm.newId){
newData.push(el);
}
})
}else{
newData.push(el);
}
});
newData = uniqBy(newData, 'id');
array1 has the type single/multiple, if the type is single, then array2 has that object one time or if the type is multiple it can be multiple times in array2.
Try following.
Convert a map for array2 where key is id and value is its occurrence
Filter array1 based on following rules
If type is multiple, then it should exist in map with occurrence more than once
Else if type is single, then it should not exist in map at all.
const array1=[{id:'q1',type:'single'},{id:'q2',type:'multiple'},{id:'q3',type:'single'},{id:'q4',type:'single'}];
const array2=[{newId:'q1',status:'submitted'},{newId:'q2',status:'drafted'},{newId:'q2',status:'submitted'},{newId:'q2',status:'submitted'},{newId:'q4',type:'drafted'}];
let a2Map = array2.reduce((a,c) => {
a[c.newId] = a[c.newId] || 0;
a[c.newId]++;
return a;
}, {});
let result = array1.filter(v => v.type === 'multiple' ? a2Map[v.id] > 1 : !a2Map.hasOwnProperty(v.id));
console.log(result);
You could take a Map and count all items with the same newId from array2. Then filter Array2 with the conditions for single or multiple values.
const
array1 = [{ id: 'q1', type: 'single' }, { id: 'q2', type: 'multiple' }, { id: 'q3', type: 'single' }, { id: 'q4', type: 'single' }],
array2 = [{ newId: 'q1', status: 'submitted' }, { newId: 'q2', status: 'drafted' }, { newId: 'q2', status: 'submitted' }, { newId: 'q2', status: 'submitted' }, { newId: 'q4', type: 'drafted' }],
map = array2.reduce((m, { newId }) => m.set(newId, (m.get(newId) || 0) + 1), new Map),
result = array1.filter(({ id, type }) =>
type === 'single' && !map.get(id) || // not in map, count: 0
type === 'multiple' && map.get(id) // in map, count: >0
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Find object in array ids

I've list of id's in array and list of article in other array.
I would like filter my article array by ids find in id's array.
Exemple :
const ids = [ '1', '2', '3' ];
const articles = [
{ id: '1', title: 'blua' },
{ id: '10', title: 'blua' }
...
];
I've try this :
ids.map((id) => {
return audits.find((audit) => {
return id === audit.id;
});
});
But return underfined :/
I think it's not a good methode ^^
Anyone can help me ?
Thank you !
Use array.prototype.filter and array.prototype.includes:
const ids = [ '1', '2', '3' ];
const articles = [ { id: '1', title: 'blua' },{ id: '10', title: 'blua' } ];
const filtered = articles.filter(a => ids.includes(a.id));
console.log(filtered);
const ids = [ '1', '2', '3' ];
const articles = [
{ id: '1', title: 'blua' },
{ id: '10', title: 'blua' }
...
];
let results = articles.filter( (a) => ids.indexOf(a.id) !== -1);

Javascript: how to merge two arrays by univocal key, and based on property?

I have two arrays: one for persons, and one to map user's ratings and valutations to any person.
Simplified, it is something like this:
var persons = [
{ id: '1', name: 'alice' },
{ id: '2', name: 'bob' },
{ id: '3', name: 'charlie' },
];
var usersToPersons = [
{ userId: '1', personId: '1', hide: true },
{ userId: '1', personId: '2', hide: false },
{ userId: '7', personId: '3' },
];
I need to extract persons not 'hidden' by current user.
To date I have come up with this (working, but quite dumb) function:
function getPersonsForUser(userId) {
var result = {};
for (var p = 0; p < persons.length; p++) {
for (var u = 0; u < usersToPersons.length; u++) {
if (usersToPersons[u].userId === userId) {
if (persons[p].id === usersToPersons[u].personId) {
if (usersToPersons[u].hide === true) {
console.warn('person', persons[p].name, 'skipped because this user hides it');
break;
} else {
result[persons[p].id] = persons[p];
}
} else {
result[persons[p].id] = persons[p];
}
} else {
result[persons[p].id] = persons[p];
}
}
}
return result;
}
console.log(getPersonsForUser('1'));
Result:
person alice skipped because this user hides it
{ '2': { id: '2', name: 'bob' },
'3': { id: '3', name: 'charlie' } }
The question is: is there any more fast/smart/slick method to filter persons by user?
With ES6 it would be like this:
let getPersonsForUser = (id) => {
let ids = usersToPersons
.filter(user => user.userId === id && !user.hide)
.map(user => user.personId);
return persons.filter(person => ids.includes(person.id));
};
You could do the same in ES5, but it will be a little more verbose.
Take a look here: Array prototype filter method
function getVisible(selUserId){
var visiblePersons = persons.filter(function(v,i,a){
var isThisPersonVisible = !usersToPersons.filter(function(vv,ii,aa){
return (vv.personId === v.id && !vv.hide && vv.userId === selUserId || !vv.userId);
}).length;
return isThisPersonVisible;
});
return visiblePersons;
}
you can try it here
demo
Hope this helps
I would use underscore library for that:
var persons = [{
id: '1',
name: 'alice'
}, {
id: '2',
name: 'bob'
}, {
id: '3',
name: 'charlie'
}, ];
var usersToPersons = [{
userId: '1',
personId: '1',
hide: true
}, {
userId: '1',
personId: '2',
hide: false
}, {
userId: '7',
personId: '3'
}, ];
function getPersonsForUser(userId) {
return _.map(
_.filter(usersToPersons,
function(doc) {
return doc.userId === userId && !doc.hide
}),
function(doc) {
return persons[doc.personId]
});
}
console.log(getPersonsForUser('1'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Categories

Resources