Desctructuring object with object arrays - javascript

I have this kind of and object:
obj: {
child1: [
{ type, checked, text, ... },
{ type, checked, text, ... },
{ type, checked, text, ... },
],
child2: [
{ type, checked, text, ... },
...
],
...
}
I need almost the same object, but child elements should have objects consisting only of type and checked values. Need my output to be like below example.
OUTPUT:
obj: {
child1: [
{
type: "type",
checked: "checked"
},
{
type: "type",
checked: "checked"
},
{
type: "type",
checked: "checked"
}
],
child2: [
{
type: "type",
checked: "checked"
}
]
}
So far everything I've tried doesn't seem to work.
My last failed attempt:
Object.keys(tabs).forEach(key =>
({
updatedState: {
[key]: (({ documentTypeId, checked }) => ({ documentTypeId, checked }))(tabs[key]),
},
}),
);

You can use Array.reduce() to iterate the object's keys, with an inner Array.map() and destructuring to create new objects from the properties you want to keep:
const type = 'type'
const checked = 'checked'
const text = 'text'
const obj = {
child1: [
{ type, checked, text },
{ type, checked, text },
{ type, checked, text },
],
child2: [
{ type, checked, text },
],
}
const result = Object.keys(obj).reduce((r, k) => {
r[k] = obj[k].map(({ type, checked }) => ({ type, checked }))
return r
}, {})
console.log(result)

You can use a combination of reduce (to iterate through the object keys) and map
(for your children arrays)
const obj = {
child1: [
{ type: 1, checked: true, text: 'aaa'},
{ type: 2, checked: false, text: 'bbb'},
{ type: 3, checked: true, text: 'ccc'}
],
child2: [
{ type: 4, checked: true, text: 'ddd'},
{ type: 5, checked: false, text: 'eee'},
{ type: 6, checked: true, text: 'fff'}
]
};
const result = Object.keys(obj).reduce((acc, key) => {
acc[key] = obj[key].map(child =>
({type: child.type, checked: child.checked}));
return acc;
}, {});
console.log(result);

Related

Merge/replace objects from different arrays if they have common property value (javascript)

I have a problem merging two objects. I work with Contentful API, and the structure of provided data for links comes not in one object so it could be easily processed further, but in separate array of objects. The connection between them is the id. The goal is to merge those objects or maybe even replace existing one.
Here is the data structure:
const dataDictionary = {
includes: {
entry: [
{
fields: {data: 'https://link1.com'},
sys: {id: 12345}
},
{
fields: {data: 'https://link2.com'},
sys: {id: 16349}
},
{
fields: {data: 'https://link3.com'},
sys: {id: 14345}
},
{
fields: {data: 'https://link4.com'},
sys: {id: 98765}
},
{
fields: {data: 'https://link5.com'},
sys: {id: 43210}
},
]
},
items: [
{
fields: {
urls: [
{
id: 14345,
type: 'link'
},
{
id: 16349,
type: 'link'
}
],
dataKey: 'dataPrpoperty',
dataKey2: 'dataPrpoperty2',
}
},
{
fields: {
urls: [
{
id: 12345,
type: 'link'
},
],
dataKey: 'dataPrpoperty',
dataKey2: 'dataPrpoperty2',
helpfulLinks: [
{
id: 98765,
type: 'link'
},
{
id: 43210,
type: 'link'
}
]
}
},
]
}
What I want to achieve is to have all arrays of objects with links inside items, been merged and mutated with actual links value from includes.
My code:
const mergeByValue = (arrayTo, arrayFrom) => {
const finalMerge = arrayTo.map(itm => ({
...arrayFrom.find((item) => (item.sys.id === itm.id) && item),
...itm
}));
return finalMerge;
}
const parseDataDictionary = (dataDictionary) => {
const pages = dataDictionary.items;
const includes = dataDictionary.includes.Entry;
pages.map(page => {
return Object.entries(page.fields).map(([key, value]) => {
if (Object.prototype.toString.call(value) === '[object Array]') {
return mergeByValue(value, includes);
}
})
})
}
parseDataDictionary(pageDictionaryData);
Seems like Everything works, I mean merging, but in the end it is not returning the merged value. If that could be possible, would be grateful for any clue.
Thanks.
UPD:
The expected result:
{
items: [
{
fields: {
urls: [
{
id: 14345,
type: 'link',
data: 'https://link3.com'
},
{
id: 16349,
type: 'link',
data: 'https://link2.com'
}
],
dataKey: 'dataPrpoperty',
dataKey2: 'dataPrpoperty2',
}
},
{
fields: {
urls: [
{
id: 12345,
type: 'link'
data: 'https://link1.com'
},
],
dataKey: 'dataPrpoperty',
dataKey2: 'dataPrpoperty2',
helpfulLinks: [
{
id: 98765,
type: 'link'
data: 'https://link4.com'
},
{
id: 43210,
type: 'link'
data: 'https://link5.com'
}
]
}
},
]
}
map return new array so if I am not wrong, you need to assign the result of your mapping and return that.
let newValues = pages.map((page) => {
return Object.entries(page.fields).map(([key, value]) => {
if (
Object.prototype.toString.call(value) ===
"[object Array]"
) {
return mergeByValue(value, includes);
}
});
});
return newValues;
Another one approach:
const dataDictionary = {includes: {entry: [{fields: {data: 'https://link1.com'},sys: {id: 12345}},{fields: {data: 'https://link2.com'},sys: {id: 16349}},{fields: {data: 'https://link3.com'},sys: {id: 14345}},{fields: {data: 'https://link4.com'},sys: {id: 98765}},{fields: {data: 'https://link5.com'},sys: {id: 43210}},]},items: [{fields: {urls: [{id: 14345,type: 'link'},{id: 16349,type: 'link'}],dataKey: 'dataPrpoperty',dataKey2: 'dataPrpoperty2',}},{fields: {urls: [{id: 12345,type: 'link'},],dataKey: 'dataPrpoperty',dataKey2: 'dataPrpoperty2',helpfulLinks: [{id: 98765,type: 'link'},{id: 43210,type: 'link'}]}},]}
const entryLinks = dataDictionary.includes.entry
.reduce((acc, { sys: { id }, fields: { data } }) => ({ ...acc, [id]: data }), {})
const addLinks = (ids) => ids.map(e => ({ ...e, data: entryLinks[e.id] }))
const updateAray = ([key, value]) => [key, Array.isArray(value) ? addLinks(value) : value]
const makeFields = (fields) => Object.fromEntries(Object.entries(fields).map(updateAray))
const makeItems = (items) => items.map(item => ({ fields: makeFields(item.fields) }))
const updateItems = (items) => ({ items: makeItems(items) })
console.log(updateItems(dataDictionary.items))
.as-console-wrapper { max-height: 100% !important; top: 0 }

Creating a new array from .map() items with certain value

In a React app, I have an array of key value pairs. Each pair corresponds to a checkbox, so when the checkbox is checked, the value of that key changes to either true or false. The data that i am pulling from is structured like:
filters: {
categories: [
{
name: "Books",
slug: "books",
selected: 0,
data: [
{ checked: false, value: "Fiction", label: "Fiction" },
{ checked: false, value: "NonFiction", label: "NonFiction" },
{ checked: false, value: "Biography", label: "Biography" },
],
},
{
name: "Movies",
slug: "movies",
selected: 0,
data: [
{ checked: false, value: "SciFi", label: "SciFi" },
{ checked: false, value: "Comedy", label: "Comedy" },
{ checked: false, value: "Romance", label: "Romance" },
],
},
{
name: "Music",
slug: "music",
selected: 0,
data: [
{ checked: false, value: "Pop", label: "Pop" },
{ checked: false, value: "Rock", label: "Rock" },
{ checked: false, value: "Alt", label: "Alt" },
],
},
],
selected: 0,
},
And I am displaying a simple list on the front-end like:
{state.theme.filters.categories.map((filter, id) => {
return (
<>
{filter.data.map((item) => {
return (
<p>{item.value}: {item.checked === true ? <span>True</span> : <span>False</span>}</p>
)
})}
</>
)
})}
What I am trying to do is create a new array that will automatically update and return only items that are checked true.
Is this something that is possible?
Yes, it is posible.
You need to filter the array.
{state.theme.filters.categories.map((filter, id) => {
return (
<>
{filter.data.filter(item => item.checked).map((item) => {
return (
<p>{item.value}: {item.checked === true ? <span>True</span> : <span>False</span>}</p>
)
})}
</>
)
})}
I think you simply want to check the checked value before returning, and not return otherwise. Something like this:
filter.data.map((item) => {
if (item.checked === true) {
return (
<p>{item.value}</p>
)
}
})

JS: slice a nested array in array of objects by index

The task is to slice a nested array by data property.
I have the following array structure:
const mockData = [
{
text: 'Text1',
data: [
{ field: '1' },
{ field: '2' },
{ field: '3' },
{ field: '4' },
{ field: '5' },
{ field: '6' }
]
},
{
text: 'Text2',
data: [{ field: '1' }, { field: '2' }, { field: '3' }, { field: '4' }]
}
];
Here's the method I use:
const sliceArray = mockData => mockData.map(d => ({...d, data: d.data.slice(0, 3)}))
It goes through all nested objects and slice array by data property, but how can I do it for a specific nested object instead of all of them?
I'd like to use text property as a key.
So, if I pass Text1 to a method - data property in the first object only should be sliced and the output should be:
const mockData = [
{
text: 'Text1',
data: [{ field: '1' }, { field: '2' }, { field: '3' }]
},
{
text: 'Text2',
data: [{ field: '1' }, { field: '2' }, { field: '3' }, { field: '4' }]
}
];
If I pass 'Text2':
const mockData = [
{
text: 'Text1',
data: [
{ field: '1' },
{ field: '2' },
{ field: '3' },
{ field: '4' },
{ field: '5' },
{ field: '6' }
]
},
{
text: 'Text2',
data: [{ field: '1' }, { field: '2' }, { field: '3' }]
}
];
What can be the solution? Thank you!
You can try to add a condition like this:
const sliceArray = (mockData, text) =>
mockData.map(d => d.text === text
? {...d, data: d.data.slice(0, 3)}
: d)
You can just add another parameter to your function and check if the text matches that parameter.
const mockData = [{"text":"Text1","data":[{"field":"1"},{"field":"2"},{"field":"3"},{"field":"4"},{"field":"5"},{"field":"6"}]},{"text":"Text2","data":[{"field":"1"},{"field":"2"},{"field":"3"},{"field":"4"}]}]
const sliceArray = (data, target, len = 3) =>
data.map(({ text, data, ...rest}) => ({
data: text == target ? data.slice(0, len) : data,
text,
...rest
}))
console.log(sliceArray(mockData, 'Text1'))
You could also pass an array of text values that you want to match and use includes method for checking.
const mockData = [{"text":"Text1","data":[{"field":"1"},{"field":"2"},{"field":"3"},{"field":"4"},{"field":"5"},{"field":"6"}]},{"text":"Text2","data":[{"field":"1"},{"field":"2"},{"field":"3"},{"field":"4"}]}, {"text":"Text3","data":[{"field":"1"},{"field":"2"},{"field":"3"},{"field":"4"},{"field":"5"},{"field":"6"}]}]
const sliceArray = (data, targets, len = 3) =>
data.map(({ text, data, ...rest}) => ({
data: targets.includes(text) ? data.slice(0, len) : data,
text,
...rest
}))
console.log(sliceArray(mockData, ['Text1', 'Text3']))
If you dont wanna to modify existing data, Since you are mocking. use reduce
const mockData = [
{
text: "Text1",
data: [
{ field: "1" },
{ field: "2" },
{ field: "3" },
{ field: "4" },
{ field: "5" },
{ field: "6" }
]
},
{
text: "Text2",
data: [{ field: "1" }, { field: "2" }, { field: "3" }, { field: "4" }]
}
];
function update(data, text, count = 3) {
return data.reduce((arr, item) => {
let updatedItem = { ...item };
if (item.text === text) {
updatedItem.data = (updatedItem.data || []).slice(0, count);
}
arr.push(updatedItem);
return arr;
}, []);
}
console.log("%j", update(mockData, "Text1"));
.as-console-row {color: blue!important}

Converting array of object to en object with key : value dynamic

I have an array of objects which look like this:
stuff = [
{ value: 'elevator', checked: true },
{ value: 'something', checked: false },
{ value: 'else', checked: true },
]
And I am trying to get something like this:
{
'elevator': true,
'something: false,
'else': true,
}
All I can get since yesterday is an array of objects like:
[
{ 'elevator': true },
{ 'something': false },
{ 'else': true }
];
I tried with mapping on array and then using Object.assign but it's not working. I get the previous code.
Use reduce
var output = stuff.reduce( (a,c) => (a[c.value] = c.checked, a) , {} )
Demo
var stuff = [
{ value: 'elevator', checked: true },
{ value: 'something', checked: false },
{ value: 'else', checked: true },
];
var output = stuff.reduce( (a,c) => (a[c.value] = c.checked, a) , {} )
console.log( output );
Edit
Using object.assign
stuff.reduce( (a,c) => Object.assign( {}, a, { [c.value] : c.checked }) , {} )
Iterate stuff array, So you will get each object under stuff. Then get that value that you need.
var stuff = [
{ value: 'elevator', checked: true },
{ value: 'something', checked: false },
{ value: 'else', checked: true },
];
var obj = {};
for( var i=0; i<stuff.length; i++) {
obj[stuff[i]['value']] = stuff[i]['checked'];
}
console.log(obj);
You can reduce function to make an single object from the given array. Just add your logic inside it. In this case, value to the property name and checked to it's value.
const stuff = [
{ value: 'elevator', checked: true },
{ value: 'something', checked: false },
{ value: 'else', checked: true },
]
const mapped = stuff.reduce((obj, item) => (obj[item.value] = item.checked, obj), {});
console.log(mapped);
If you can use ES6, you can do it with Object.assign, array.prototype.map, some destructuring, object litteral dynamic key and spread operator:
var stuff = [
{ value: 'elevator', checked: true },
{ value: 'something', checked: false },
{ value: 'else', checked: true },
];
var result = Object.assign({}, ...stuff.map(({value, checked}) => ({[value]: checked})));
console.log(result);

Efficient way to do the filter using loadash or any other library?

I am filtering array whenever checkboxes are checked. There are totally 7 checkboxe each is associated with an object.
here is my code,
if (this.deliveryConcession[0].checked) {
this.allItems = this.allItems.filter(fil => fil.deliveryconcession.readytoship === this.deliveryConcession[0].checked);
}
if (this.deliveryConcession[1].checked) {
this.allItems = this.allItems.filter(fil => fil.deliveryconcession.instantdownload === this.deliveryConcession[1].checked);
}
if (this.deliveryConcession[2].checked) {
this.allItems = this.allItems.filter(fil => fil.deliveryconcession.unespecifiedshipment === this.deliveryConcession[2].checked);
}
if (this.seatConcession[0].checked) {
this.allItems = this.allItems.filter(fil => fil.seatingConcession.parking === this.seatConcession[0].checked);
}
if (this.seatConcession[1].checked) {
this.allItems = this.allItems.filter(fil => fil.seatingConcession.restrictedview === this.seatConcession[1].checked);
}
if (this.seatConcession[2].checked) {
this.allItems = this.allItems.filter(fil => fil.seatingConcession.wheelchair === this.seatConcession[2].checked);
}
if (this.seatConcession[3].checked) {
this.allItems = this.allItems.filter(fil => fil.seatingConcession.alcoholFree === this.seatConcession[3].checked);
}
here is my objects for filter,
seatConcession = [
{ id: 1, name: 'Parking pass included', checked: false },
{ id: 2, name: 'Unrestricted view', checked: false },
{ id: 3, name: 'Wheel chair accessible', checked: false },
{ id: 4, name: 'Without age restrictions', checked: false }
];
deliveryConcession = [
{ id: 1, name: 'Ready to ship(paper)', checked: false },
{ id: 2, name: 'Instant download(e-ticket)', checked: false },
{ id: 3, name: 'Unspecified shipment(paper)', checked: false }
];
how can i improve the above with simple loadash filter or another way?
let keys = [
["readytoship", "deliveryConcession"],
["instantdownload", "deliveryConcession"],
/* and so on, make sure to order */
];
this.allItems.filter(item => {
return keys.every((arr, i) => {
let [k, attr] = arr;
return item[attr][k] === this[attr][i].checked;
});
});
You will need to order the keys array appropriately. But now it's a two-liner. Other than let and the arrow functions this is all valid ES 5, no lodash required.
EDIT
Since you still haven't actually posted the relevant code this is still something of a stab in the dark, but taking your sample input of
seatConcession = [
{ id: 1, name: 'Parking pass included', checked: false },
{ id: 2, name: 'Unrestricted view', checked: false },
{ id: 3, name: 'Wheel chair accessible', checked: false },
{ id: 4, name: 'Without age restrictions', checked: false }
];
deliveryConcession = [
{ id: 1, name: 'Ready to ship(paper)', checked: false },
{ id: 2, name: 'Instant download(e-ticket)', checked: false },
{ id: 3, name: 'Unspecified shipment(paper)', checked: false }
];
And assuming you have a list of which checkboxes are checked that is ordered in the same order as the objects like so
let checked = [true, false, true, true, false /* etc */];
You want to do something like this:
let filtered = seatConcessions
.concat(deliveryConcession)
.filter((obj, i) => checked[i]);
You will have to adapt this to your specific case (again, since the sample input you put up is different than the code you wrote), but is a pattern for doing this in general.

Categories