JS merge two subarray using map - javascript

I use the following to merge two arrays:
var mySeries = [
{ name: '4', data: [4] },
{ name: '3', data: [3] }
];
var mySeries1 = [
{ name: '5', data: [0] },
{name: '4', data:[0]},
{name: '3', data:[0]},
{name: '2', data:[0]},
{ name: '1', data: [0] }
];
var res = mySeries1.map(obj => mySeries.find(o => o.name === obj.name) || obj);
console.log(res);
Works great; however, my challenge is my array is structured as:
var myArray = [{
mySeries : [
{ name: '4', data: [4] },
{ name: '3', data: [3] }],
mySeries1 : [
{ name: '5', data: [0] },
{ name: '4', data: [0] },
{ name: '3', data: [0] },
{ name: '2', data: [0] },
{ name: '1', data: [0] }]
}];
];
So I need to map subarrays, tried using the following:
var res = myArray.mySeries1.map(obj => myArray.mySeries.find(o => o.name === obj.name) || obj);
I get this error:
Cannot read properties of undefined (reading 'map')
How can I point to the subarray?

var myArray = [{
mySeries : [
{ name: '4', data: [4] },
{ name: '3', data: [3] }],
mySeries1 : [
{ name: '5', data: [0] },
{ name: '4', data: [0] },
{ name: '3', data: [0] },
{ name: '2', data: [0] },
{ name: '1', data: [0] }
]
}];
var res = myArray[0].mySeries1.map(obj => myArray[0].mySeries.find(o => o.name === obj.name) || obj);
console.log(res)

you could change your array to dictionary and iterate it like you already did.
var myArray = {
mySeries : [
{ name: '4', data: [4] },
{ name: '3', data: [3] }],
mySeries1 : [
{ name: '5', data: [0] },
{ name: '4', data: [0] },
{ name: '3', data: [0] },
{ name: '2', data: [0] },
{ name: '1', data: [0] }]
};
i.e. var res = myArray.mySeries1.map(obj => myArray.mySeries.find(o => o.name === obj.name) || obj);

Related

Combine 2 arrays based on their common id [duplicate]

This question already has answers here:
Merge two array of objects based on a key
(23 answers)
Closed 4 months ago.
I have 2 different arrays, that i want to combine.
The first one looks like following:
const Cats[] = [
{ id: '1', name: 'Smiley' },
{ id: '2', name: 'Purple' },
]
the second one:
const catAges[] = [
{ id: '4', age: '13', catId: '1' },
{ id: '5', age: '4', catId: '2' },
];
and i want to combine them where id from Cats[] and catId from catAges[] are the same and have a result like following:
{ id: '4', age: '13', cat: { id: '1', name: 'Smiley' } },
{ id: '5', age: '4', cat: { id: '2', name: 'Purple' } },
i get the arrays from 2 different async functions looking like this:
const cats = [await getByCatId("1"), await getByCatId("2")];
const catsAge = await getCatsAges();
But i need help in how i combine these 2 and map them. I've tried something like this but without any success:
const all = (cats, catsAge) =>
cats.map(cats=> ({
...catsAge.find((cats) => (catsAge.catId === cats.id) && catsAge),
...cats
}));
console.log(all(cats, catsAge));
Thankful for any help in how to move forward.
const Cats = [
{ id: '1', name: 'Smiley' },
{ id: '2', name: 'Purple' },
]
const catAges = [
{ id: '4', age: '13', catId: '1' },
{ id: '5', age: '4', catId: '2' },
];
const transformed = catAges.map(item => {
const cat = Cats.find(cat => cat.id === item.catId);
if (cat) {
item.cat = cat;
delete item.catId;
}
return item;
});
console.log(transformed);
The problem with your function is just that you're re-using the cats variable too much, so in your .find comparision you're comparing an element from catsAge (as cats.id) and the catsAge array (as catsAge.catId) which is undefined.
Try this:
const all = (cats, catsAge) =>
cats.map((cat) => ({
...catsAge.find((catsAge) => catsAge.catId === cat.id),
...cat,
}));
Pro tip: Learn+Use Typescript and the compiler would catch these errors for you :)
const Cats = [
{ id: '1', name: 'Smiley' },
{ id: '2', name: 'Purple' },
]
const catAges = [
{ id: '4', age: '13', catId: '1' },
{ id: '5', age: '4', catId: '2' },
];
catAges.map(catage => {
const cat = Cats.find(c => c.id == catage.catId);
if(cat) {
delete catage.catId;
catage.cat = cat;
return catage;
}
});

Count array with matching data then push to new array

I need to figure out how to count an array then combine only the matching ones.
example
const info = [ { name: 'John', date:'2022-04-11', type: '2', time: 5.00 },
{ name: 'Dave', date:'2022-04-12', type: '3', time: 6.00 },
{ name: 'John', date:'2022-04-11', type: '2', time: 2.00 },
{ name: 'John', date:'2022-04-15', type: '2', time: 3.00 } ];
The expected result should check for the same type, name and date them combine time.
and the new array should look something like this.
It can be done with a forloop, but I would like to try creating a solution with es6.
But I am a bit unsure how to approach this.
const expected = [ { name: 'John', date:'2022-04-11', type: '2', time: 7.00 },
name: 'Dave', date:'2022-04-12', type: '3', time: 6.00 },
name: 'John', date:'2022-04-15', type: '2', time: 3.00 } ];
You could use a combination of .reduce and .find
const info = [ { name: 'John', date:'2022-04-11', type: '2', time: 5.00 },
{ name: 'Dave', date:'2022-04-12', type: '3', time: 6.00 },
{ name: 'John', date:'2022-04-11', type: '2', time: 2.00 },
{ name: 'John', date:'2022-04-15', type: '2', time: 3.00 } ];
const result = info.reduce((acc, x) => {
const foundObj = acc.find(y => y.name === x.name && y.date === x.date && y.type === x.type);
if (foundObj) {
foundObj.time += x.time;
} else {
acc.push(x);
}
return acc;
}, []);
console.log(result)
You could for example create an object, where the key is a combination of name, date, type and the value is time
let grouped = info.reduce((acc, curr) => {
let key = `${curr.name}${curr.date}${curr.type}`;
if (!acc[key]) {
acc[key] = {
name: curr.name,
date: curr.date,
type: curr.type,
time: curr.time,
};
} else {
acc[key].time += curr.time;
}
return acc;
}, {});
let expected = Object.values(grouped);

Filter does not return the correct result

I have this array and I created this function that return me the filtered array:
const result = [{
key: 'A',
title: 'titleA',
data: [{
name: 'miael',
id: 'id4',
},
{
name: 'top',
id: 'id2',
}
]
},
{
key: 'B',
title: 'titleB',
data: [{
name: 'mich1',
id: 'id12',
},
{
name: 'tomato',
id: 'id123',
}
]
},
]
const doSearch = (data) => result.filter(entry =>
entry.data.some(item =>
item.name
.toString()
.toLowerCase()
.includes(data.toString().toLowerCase().trim()),
),
);
console.log(doSearch('mich'));
This works, but it also returns results that do not contain the searched word 'mic'
if I search for mic, I expect this result:
[{
key: 'B',
title: 'titleB',
data: [{
name: 'mich1',
id: 'id12',
},
]
}],
what am I doing wrong?
A couple of changes should make this work the way you wish.
Turning doSearch into a function.
Adding a searchFor parameter to the doSearch() function and passing to the .includes() call.
Using Array.reduce() to create the output array. Items are only added if they include the searchFor value.
const input = [{ key: 'A', title: 'titleA', data: [{ name: 'miael', id: 'id4', }, { name: 'top', id: 'id2', } ] }, { key: 'B', title: 'titleB', data: [{ name: 'mich1', id: 'id12', }, { name: 'tomato', id: 'id123', } ] }, ]
const doSearch = (searchFor, arr) => arr.reduce((acc, { key, title, data }) => {
const filteredData = data.filter(({ name }) => {
return name.toLowerCase().includes(searchFor.toLowerCase())
});
if (filteredData.length > 0) {
acc.push({ key, title, data: filteredData });
}
return acc;
}, []);
console.log(doSearch('mic', input ));
You can keep your current logic and add a map with the same filter for entry.data:
const result = [{
key: 'A',
title: 'titleA',
data: [{
name: 'miael',
id: 'id4',
},
{
name: 'top',
id: 'id2',
}
]
},
{
key: 'B',
title: 'titleB',
data: [{
name: 'mich1',
id: 'id12',
},
{
name: 'tomato',
id: 'id123',
}
]
},
]
function nameFilter(item, data) {
return item.name
.toString()
.toLowerCase()
.includes(data.toString().toLowerCase().trim())
}
const doSearch = (data) => result.filter(entry =>
entry.data.some(item =>
nameFilter(item, data)
),
).map(entry => ({
...entry,
data: entry.data.filter(item => nameFilter(item, data))
}));
console.log(doSearch('mich'));

Filter array of objects by array of strings

My Object:
const searchFor = ['appInfo', 'questions'];
My Data:
const data = [
{ id: '1', data: 'jobInfo' },
{ id: '2', data: 'appInfo' },
{ id: '3', data: 'documents' },
{ id: '4', data: 'questions' },
];
I am expecting the final result to be:
[
{ id: '2', data: 'appInfo' },
{ id: '4', data: 'questions' },
];
My try was that I can filter with one item with fixed value
const result = Object.keys(searchFor).map((key) => data.filter((obj) => obj.data === key))
I getting array of arrays but I need array of object and more over I not sure if there is any better way to do this.
Thanks in advance :)
You may use Array.prototype.filter() together with Array.prototype.includes():
const searchFor = ['appInfo', 'questions'],
data = [{id:'1',data:'jobInfo'},{id:'2',data:'appInfo'},{id:'3',data:'documents'},{id:'4',data:'questions'},],
result = data.filter(({data:d}) => searchFor.includes(d))
console.log(result)
.as-console-wrapper{min-height:100%;}
You can use the Array methods .filter and .includes (because searchFor is an Array.)
const searchFor = ['appInfo', 'questions'];
const data = [
{ id: '1', data: 'jobInfo' },
{ id: '2', data: 'appInfo' },
{ id: '3', data: 'documents' },
{ id: '4', data: 'questions' },
];
const result = data.filter(item => searchFor.includes(item.data));
console.log(result);
You could take a Map for the key/objects pairs and map the found objects.
const
searchFor = ['appInfo', 'questions'],
data = [{ id: '1', data: 'jobInfo' }, { id: '2', data: 'appInfo' }, { id: '3', data: 'documents' }, { id: '4', data: 'questions' }],
result = searchFor.map(
Map.prototype.get,
new Map(data.map(o => [o.data, o]))
);
console.log(result);
You can do this:
const searchFor = ['appInfo', 'questions'];
const data = [
{ id: '1', data: 'jobInfo' },
{ id: '2', data: 'appInfo' },
{ id: '3', data: 'documents' },
{ id: '4', data: 'questions' },
];
var res = data.filter(i=> searchFor.includes(i.data))
console.log(res)
If you wanna reduce the complexity to O(N), you may consider this:
const searchFor = ['appInfo', 'questions'];
const data = [
{ id: '1', data: 'jobInfo' },
{ id: '2', data: 'appInfo' },
{ id: '3', data: 'documents' },
{ id: '4', data: 'questions' },
];
var hashTable = {};
searchFor.forEach(i=> hashTable[i] = true );
var res = data.filter(i=>{
return hashTable[i.data]
});
console.log(res)
You can use:
const results = data.filter(item => searchFor.includes(item.data)):
console.log(results):
const result = data.filter((rec, id) => {
if (searchFor.indexOf(rec.data) > -1) {
return rec
}
})
console.log(data.filter(element=>{
return searchFor.includes(element.data)
}));
The idea here is you have to take elements of data and then have to filter each element of data after matching with search for object .since search for the object that contains your filter criteria so it has to be nested within external iteration i.e filter method we have passed on data object .

Javascript filter method, Remove all Items with matching values in array

I'm trying to remove all items if they match with array values but it's removing only one item. How can i remove all items with filter method or what is the best way to achieve this.
let data = [
{
id: '1',
title: 'ABC'
},
{
id: '2',
title: 'DEF'
},
{
id: '3',
title: 'GHI'
},
{
id: '4',
title: 'JKL'
},
{
id: '5',
title: 'MNO'
}
]
data = data.filter(post => {
let remove = ['2', '4', '5']
for(let i = 0; i < remove.length; i++) {
return post.id !== remove[i]
}
})
console.log(data)
Thanks
you should return false if you want to remove item from array
let data = [
{
id: '1',
title: 'ABC'
},
{
id: '2',
title: 'DEF'
},
{
id: '3',
title: 'GHI'
},
{
id: '4',
title: 'JKL'
},
{
id: '5',
title: 'MNO'
}
]
let remove = ['2', '4', '5']
data = data.filter(post => {
return !remove.includes(post.id);
})
console.log(data)
All the notice are in the snippet's comment
let data = [ { id: '1', title: 'ABC' }, { id: '2', title: 'DEF' }, { id: '3', title: 'GHI' }, { id: '4', title: 'JKL' }, { id: '5', title: 'MNO' } ]
const remove = ['2', '4', '5']
// `indexOf` is from ES5
data = data.filter(post => remove.indexOf(post.id) === -1)
console.log(data)
// `includes` is from ES7
data = data.filter(post => !remove.includes(post.id))
console.log(data)
// this will recreate the array ['2', '4', '5'] 5 times
data = data.filter(post => !['2', '4', '5'].includes(post.id))
console.log(data)
There is no need to use for loop inside of filter.
Instead it is possible to use some method inside of filter. The some method checks whether at least one element satisfies condition inside of provided function. So unnecessary iteration will be avoided:
data.filter(f => !remove.some(s => s == f.id))
An example:
let data = [
{
id: '1',
title: 'ABC'
},
{
id: '2',
title: 'DEF'
},
{
id: '3',
title: 'GHI'
},
{
id: '4',
title: 'JKL'
},
{
id: '5',
title: 'MNO'
}
]
let remove = ['2', '4', '5']
console.log(data.filter(f => !remove.some(s => s == f.id)));
I'll suggest using includes rather then a nested for loop.
You should also move the remove var outside of the loop, so it's not reinitialised every time.
The callback to the filter method is a predicate. If the condition evaluates to true, the current value in the iteration will be returned. In your case, you want to return if the current value is not in the remove array.
let data = [
{
id: '1',
title: 'ABC'
},
{
id: '2',
title: 'DEF'
},
{
id: '3',
title: 'GHI'
},
{
id: '4',
title: 'JKL'
},
{
id: '5',
title: 'MNO'
}
]
const remove = ['2', '4', '5']
data = data.filter(post => {
return !remove.includes(post.id)
})
console.log(data)

Categories