Is there a good way to combine array and object in JavaScript? - javascript

There is an object and an array.
list: [
{
oldData: {
title: 'abc',
id: 1,
date: '1982-09-30',
budget: 250000,
},
newData: [
{
key: 1,
data: null,
value: 5,
},
{
key: 2,
data: null,
value: 22,
},
...
],
},
{
oldData: {
title: 'blablablaaaaa',
id: 2,
date: '2012-02-23',
budget: 350000,
},
newData: [
{
key: 1,
data: null,
value: 35,
},
{
key: 2,
data: null,
value: 41,
},
...
],
},
... some more datas...
]
as above, There is more data of the same type.
I need to use oldData and newData together, so I want to combine the two.
How can I combine oldData and newData so that there are multiple sets of oldData and newData pairs?
for example, [{ combineData: {...} }, { combineData: {...} }, ... }] here.
I know how to combine array and array, object and object, but I do not know how to do that.
Is there any good solution?

Your desired result is unclear, but you did say you wanted old/new pairs. This answer is different than the others in that it produces an array of combined data objects made of old/new pairs, where the oldData values are duplicated in order to appear alongside each corresponding newData value within its list item.
your original data after the first question update:
let list = [
{
oldData: { title: 'abc', id: 1, date: '1982-09-30', budget: 250000 },
newData: [
{ key: 1, data: null, value: 5 },
{ key: 2, data: null, value: 22 },
//...
],
},
{
oldData: { title: 'blablablaaaaa', id: 2, date: '2012-02-23', budget: 350000 },
newData: [
{ key: 1, data: null, value: 35 },
{ key: 2, data: null, value: 41 },
//...
],
},
//... some more datas...
];
This code maps each {old,new[]} list item into arrays of pairs [{old,new}, {old,new}, ...] that are combined in the final reduce() call:
var combinedDatas = list
.map(listItem => listItem.newData.map(newItem => ({
oldData: listItem.oldData,
newData: newItem
})))
.reduce();
console.log(JSON.stringify(oldNewCombos, null, 4));
produces a list of denormalized pairs:
[
{ list[0].oldData, list[0].newData[0] },
{ list[0].oldData, list[0].newData[1] },
//...rest of list[0] oldData with newData[n] combos
{ list[1].oldData, list[1].newData[0] },
{ list[1].oldData, list[1].newData[1] },
//...rest of list[1] oldData with newData[n] combos
{ list[2].oldData, list[2].newData[0] },
{ list[2].oldData, list[2].newData[1] },
//...rest of list[2] oldData with newData[n] combos
//...
]

You can use map() on the array. And use Object.assign() and spread operator to combine all the properties of all the elements in newData into one object.
const arr = [
{
oldData: {
a:10,
b:20
},
newData: [
{
c:30,
d:40
},
{
e:50,
f:60
}
],
}
]
const res = arr.map(x => ({combinedData:{...x.oldData, ...Object.assign({}, ...x.newData)}}))
console.log(res)

You can map over the array and use object destructuring (...object) to create a new, combined object:
const data = [
{
oldData: {
foo: 'lorem',
},
newData: {
bar: 'ipsum',
},
},
{
oldData: {
foo: 'dolor',
},
newData: {
bar: 'sit amet',
},
},
];
const combined = data.map(record => ({...record.oldData, ...record.newData}));
console.log(combined);
This will overwrite duplicate keys however, so something like:
{
oldData: {
message: 'a',
},
newData: {
message: 'b',
},
}
will become:
{
message: 'b',
}

Related

Advanced filtering nested elements in TypeScript/JavaScript

Given the following structure and data:
interface GrandChild {
id: number,
values: Array<string>,
}
interface Child {
id: number,
subItems: Array<GrandChild>
}
interface Foo {
items: Array<Child>
}
const data: Foo = {
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 2, subItems: [ { id: 20, values: ['REMOVE', 'REMOVE'] }, { id: 21, values: ['REMOVE'] } ] },
{ id: 3, subItems: [ { id: 30, values: ['REMOVE'] }, { id: 31, values: ['REMOVE'] }, { id: 32, values: ['REMOVE', '32'] } ] },
]
};
How can I use the Array's methods (filter, map, some, etc.) to achieve the following result?
const expected: Foo = {
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 3, subItems: [ { id: 32, values: ['32'] } ] },
]
}
So far, I filtered the resulting data, removing the undesired elements, as following:
const filteredData: Foo = {
...data,
items: data.items.map(item => ({
...item,
subItems: item.subItems.map(subItem => ({
...subItem,
values: subItem.values.filter(value => value !== 'REMOVE')
}))
}))
}
Resulting:
{
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 2, subItems: [ { id: 20, values: [] }, { id: 21, values: [] } ] },
{ id: 3, subItems: [ { id: 30, values: [] }, { id: 31, values: [] }, { id: 32, values: ['32'] } ] },
]
};
But, I cannot figure a way out to remove the empty subItems elements without looping through the result.
You can check online the above code here.
If you really want to do it just with filter and map, add a filter after each of your maps to remove subItems that have an empty values array and to remove items that have an empty subItems array:
const filteredData = {
...data,
items: data.items
.map((item) => ({
...item,
subItems: item.subItems
.map((subItem) => ({
...subItem,
values: subItem.values.filter((value) => value !== "REMOVE"),
}))
.filter(({ values }) => values.length > 0), // ***
}))
.filter(({subItems}) => subItems.length > 0), // ***
};
But:
When I have map followed by filter, I always ask myself if the data is large enough that I should avoid making multiple passes through it.
When I'm doing lots of nesting of map calls and such, I always ask myself if it would be clearer when reading the code later to use simpler, smaller loops.
Here's what you might do if answering "yes" to either or both of those questions:
const filteredData: Foo = {
...data,
items: [],
};
for (const item of data.items) {
const subItems: Array<GrandChild> = [];
for (const subItem of item.subItems) {
const values = subItem.values.filter((value) => value !== "REMOVE");
if (values.length) {
subItems.push({
...subItem,
values,
});
}
}
if (subItems.length > 0) {
filteredData.items.push({
...item,
subItems,
});
}
}

JavaScript Array Filtering in Nested Arrays

I have an array that looks something like this:
const arrayObj = [
{
id: 1,
itemsList: [
{
name: "Paul",
},
{
name: "Newman",
},
],
},
{
id: 2,
itemsList: [
{
name: "Jack",
},
{
name: "Man",
},
],
},
]
What I want is to filter the objects whose itemsList contain an object with the name of a certain value. For example, I want to be able to filter out an array with objects whose inner objects with names that contain "ul" (in this case the name Paul contains "ul"), it should give me an output as such:
const outputArray = [
{
id: 1,
itemsList: [
{
name: "Paul",
},
{
name: "Newman",
},
]
}
]
So far, I've only been able to filter out a simple flat array of objects with this function:
function filterByName(array: any, string: any) {
return array.filter((obj: any) =>
["name"].some((key: any) =>
String(obj[key]).toLowerCase().includes(string.toLowerCase())
)
);
}
but I don't know how to apply it to my case.
Here you can use the some method combined with the includes method
const arrayObj = [{
id: 1,
itemsList: [{
name: "Paul",
},
{
name: "Newman",
},
],
},
{
id: 2,
itemsList: [{
name: "Jack",
},
{
name: "Man",
},
],
},
]
const getFilterArray = (name) => {
return arrayObj.filter(obj => obj.itemsList.some(x => x.name.toLowerCase().includes(name.toLowerCase())))
}
console.log(getFilterArray("ul"))
const result = arrayObj.filter(({ itemsList }) =>
itemsList.some(({ name }) => name.toLowerCase().includes('ul')));
Can you try this?

ReactJs | Array of Arrays deconstruction problem

Situation is simple: Make a double map to deconstruct into a single Array rather then multiple.
The Data example:
const data = [
{
name: 'testa',
values: [
{ index: '1993', value: 5 },
{ index: '1994', value: 6 },
],
},
{
name: 'testb',
values: [
{ index: '1991', value: 8 },
{ index: '1992', value: 3 },
{ index: '1993', value: 9 },
],
},
];
A method should take this array and convert into a single array of this data (order dosent matter):
const proccessedData = [
{ index: '1993', value: 5, name: 'testa' },
{ index: '1994', value: 6, name: 'testa' },
{ index: '1991', value: 8, name: 'testb' },
{ index: '1992', value: 3, name: 'testb' },
{ index: '1993', value: 9, name: 'testb' },
];
I achieve this by using this method I created:
const getData = (obj) => {
const data = [];
obj.map(({ values, name }) => {
data.push(...values.map(({ index, value }) => ({ index, value, name })));
});
return data;
};
And it works BUT I disliake it because of (read below in The Problem):
THE PROBLEM
It depends on const data = [] to do a data.push(...). Would prefer that it would auto decontruct it so a method would look something like this:
const getData = (obj) =>
obj.map(({ values, name }) => values.map(({ index, value }) => ({ index, value, name })));
Basically, on a single line, without the use of another variable BUT the return structure would remain as mentioned above. But using thtis method it returns an array with 2 different arrays inside.
It is being used inside another structure like so:
const config = {
data: getData(data),
height: 400,
xField: "index",
yField: "value",
seriesField: "name"
};
So it has to return deconstructed array already without involving any other variables. Yes, I could leave it as it is right now, but I wanna do the deconstruction way as its cleaner, in my opinion, and I would learn something new.
NOTE doing data: {...getData(data)}, or data: [...getData(data)], does Not work.
A working example to play around: https://codesandbox.io/s/bold-cannon-fwot6?file=/src/App.js:690-826
This would be simpler with flatMap:
const data = [
{
name: 'testa',
values: [
{ index: '1993', value: 5 },
{ index: '1994', value: 6 },
],
},
{
name: 'testb',
values: [
{ index: '1991', value: 8 },
{ index: '1992', value: 3 },
{ index: '1993', value: 9 },
],
},
];
proccessedData = data.flatMap(d => d.values.map(v => ({...v, name: d.name})))
console.log(proccessedData)
You can also try this: I am using here map function twice, and then flatten into a single array, The flatMap method is identical to a map followed by a call to a flat of depth 1.
const data = [
{
name: 'testa',
values: [
{ index: '1993', value: 5 },
{ index: '1994', value: 6 },
],
},
{
name: 'testb',
values: [
{ index: '1991', value: 8 },
{ index: '1992', value: 3 },
{ index: '1993', value: 9 },
],
},
];
const dataProcess = data
.map((d) =>
d.values.map((v) => ({...v, name: d.name}))
).flat();
console.log(dataProcess);

Javascript Alphabetised Object Key Sorting

I'm wondering, I have the following data structure:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
...
{
name: 'Zulu',
},
{
name: 'Zulo',
},
]
I'm expecting there to be at least one, usually more, key for each letter of the alphabet. However, if there isn't a single data.name I would still like in the below data structure to have an empty domains array [].
I was wondering, how could this be manipulated into the following data structure:
data = {
a: {
domains: [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
],
},
b: {
domains: [
...
]
},
...
z: {
domains: [
...
]
},
};
I have used a few methods, which involved a pre-constructed "alphbetised" key = object array, then filtered each on the first letter of the data.name value...but I was wondering if there was a standard and performant method to acheive this?
Using reduce()
const data = [{name:"Alpha"},{name:"Alfa"},{name:"Bravo"},{name:"Brafo"},{name:"Charlie"},{name:"Charly"},{name:"Zulu"},{name:"Zulo"}]
const res = data.reduce((a, v) => {
// prepare key
let key = v.name.substring(0,1).toLowerCase()
// check key in accumulator
if (!a[key]) {
// assign domain object
a[key] = {domains: []}
}
// push domain array
a[key].domains.push(v)
return a
}, {})
console.log(res)
Here is what you want:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
{
name: 'Zulu',
},
{
name: 'Zulo',
},
];
console.log(data.reduce((a, c) => {
const firstLetter = c.name[0].toLowerCase();
if (a[firstLetter]) {
a[firstLetter].domains.push(c);
} else {
a[firstLetter] = { domains: [c] };
}
return a;
}, {}));

Return deconstructed array in object with es6 map

I want to return a deconsturcted array so I only get single element in te returned array instead of array.
const data = [
{
title: 'amsterdam',
components: [
{
id: 1,
name: 'yanick',
},
{
id: 2,
name: 'ronald',
},
],
},
{
title: 'rotterdam',
components: [
{
id: 4,
name: 'nicky',
},
{
id: 3,
name: 'casper',
},
],
},
];
const test = data
.map(item => {
console.log(item.components);
return item.components;
}).map(array => {
// how to get comibned components here?
// it can't use ...item.components (deconstructing or something)
});
console.log('test', test);
So I want to use chained map functions to create one array of all elements in item.components. Is this possible? Seems like I can't deconstruct the array of each item.
Array.prototype.reduce seems like the correct method to use in this context.
const test = data.reduce( (result, current) => result.concat(current.components) , []);
console.log('test', test);
Output
test [ { id: 1, name: 'yanick' },
{ id: 2, name: 'ronald' },
{ id: 4, name: 'nicky' },
{ id: 3, name: 'casper' } ]
Get the components with Array.map(), and flatten by spreading into Array.concat():
const data = [{"title":"amsterdam","components":[{"id":1,"name":"yanick"},{"id":2,"name":"ronald"}]},{"title":"rotterdam","components":[{"id":4,"name":"nicky"},{"id":3,"name":"casper"}]}];
const result = [].concat(...data.map(o => o.components));
console.log(result);
To get data combined into single array you can use reduce in combination with concat that will create a single array of results.
const data = [
{
title: 'amsterdam',
components: [
{
id: 1,
name: 'yanick',
},
{
id: 2,
name: 'ronald',
},
],
},
{
title: 'rotterdam',
components: [
{
id: 4,
name: 'nicky',
},
{
id: 3,
name: 'casper',
},
],
},
];
const test = data
.map(item => {
return item.components;
}).reduce((res, item) => {
return res.concat(item);
}, []);
console.log('test', test);

Categories