I have problem when set value in nested map, the assigned value only take the last index value. Is that something I do wrong or I miss?Thank you
Here is my data:
const items = [{
id: 'item1'
}, {
id: 'item2'
}]
const itemDetails = [{
name: 'data A',
class: 'A'
}, {
name: 'data B',
class: 'B'
}, {
name: 'data C',
class: 'C'
}]
The result I expect is:
[
[
{ name: 'data A', class: 'A', itemIndex: 0, itemId: 'item1' },
{ name: 'data B', class: 'B', itemIndex: 0, itemId: 'item1' },
{ name: 'data C', class: 'C', itemIndex: 0, itemId: 'item1' }
],
[
{ name: 'data A', class: 'A', itemIndex: 1, itemId: 'item2' },
{ name: 'data B', class: 'B', itemIndex: 1, itemId: 'item2' },
{ name: 'data C', class: 'C', itemIndex: 1, itemId: 'item2' }
]
]
But I got this result using nested map:
[
[
{ name: 'data A', class: 'A', itemIndex: 1, itemId: 'item2' },
{ name: 'data B', class: 'B', itemIndex: 1, itemId: 'item2' },
{ name: 'data C', class: 'C', itemIndex: 1, itemId: 'item2' }
],
[
{ name: 'data A', class: 'A', itemIndex: 1, itemId: 'item2' },
{ name: 'data B', class: 'B', itemIndex: 1, itemId: 'item2' },
{ name: 'data C', class: 'C', itemIndex: 1, itemId: 'item2' }
]
]
My Code:
const result = items.map((item, itemIdx) => {
return itemDetails.map(detail => {
detail.itemIndex = itemIdx
detail.itemId = item.id
return detail
})
})
Less code:
const result = items.map((item, itemIndex) =>
itemDetails.map(detail => ({...detail, itemIndex, itemId: item.id})))
Please first create a copy of the original detail and then mutate it.
This should work fine:
const result = items.map((item, itemIdx) => {
return itemDetails.map(detail => {
const newDetail = {...detail}
newDetail.itemIndex = itemIdx
newDetail.itemId = item.id
return newDetail;
})
})
var resArr=[];
// I am using .length in loops if your data is large you can precalculate the values // intovariables and use them
for(var i=0;i<items.length;i++){
const id = items[i]["id"];
var temparr=[]
for(var j=0;j<itemDetails.length;j++){
let obj={"name":itemDetails[j]["name"],"class":itemDetails[j]["class"],"itemIndex":i,"itemId":id}
temparr.push(obj);}
resArr.push(temparr)
}
console.log(resArr);
Related
I have an array that looks something like this
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
As you can see, the organization name is something I want to key off of and create a data structure like this:
const output = [
// data.value will be their ID
{
organizationName: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' },
],
},
{
organizationName: 'Organization B',
data: [
{ label: 'Person 3', value: '3' },
],
},
]
What I've tried
I know I want to use reduce for something like this, but I feel like I'm off:
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
if (organizationName) {
acc.push({ organization: organizationName, data: [] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
}
return acc;
}, [] as any);
However the output comes out to something like this:
[
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization B', data: []},
];
data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?
Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.
const externalPeople = {
data : [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
],
};
const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
name: externalPersonName,
id
} = currentValue;
// Is the org new? Yes, create an entry for it
if (!acc[organizationName]) {
acc[organizationName] = { organization: organizationName, data: [] };
}
// push the person to the organization
acc[organizationName].data.push({ name: externalPersonName, value: id });
return acc;
}, {}));
console.log(providerOptions)
Here is another solution
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const result = example.reduce((res, entry) => {
const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
if(recordIndex >= 0) {
res[recordIndex].data.push({ label: entry.name, value: entry.id});
} else {
const record = {
organizationName: entry.organization.name,
data: [{ label: entry.name, value: entry.id }]
};
res.push(record);
}
return res;
}, []);
console.log(result);
You are not checking if the value is already present in your accumulation acc
You can check it with a simple find in the if statement since it's an array
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
//Check if organization is not present already
if (!acc.find(a => a.organization === organizationName)) {
//Add also the data of the element your are processing
acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
}
return acc;
}, [] as any);
I also added the data of the first element of the group you create when adding the organization.
The result should be as your expected output:
[
{
organization: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' }
]
},
{
organization: 'Organization B',
data: [
{ label: 'Person 3', value: '3' }
]
}
]
Hope it helps!
Compare this solution (using Lodash) with other solutions. Which one emphasises your intentions at most? This is why we use Lodash in our company - to maintain code as declarative as we can, because code readability, with minimum cognitive overload, is most important goal during coding.
const persons = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const personsByOrganizations = _.groupBy(persons, 'organization.name')
const output = _.map(personsByOrganizations, (persons, organizationName) => ({
organizationName,
data: _.map(persons, ({ name, id }) => ({
label: name,
value: id
}))
}))
Something like that with using a Set?
result = [...new Set(example.map(d => d.organization.name))].map(label => {
return {
organizationName: label,
data: example.filter(d => d.organization.name === label).map(d => {
return {label: d.name, value: d.id}
})
}
})
`
I have an array of objects that I'm trying to rebuild without any success:
const data = [
{
ID: 1,
TemplateName: 'Template 1',
TemplateCategory: 'Category A',
},
{
ID: 2,
TemplateName: 'Template 2',
TemplateCategory: 'Category A',
},
{
ID: 3,
TemplateName: 'Template 3',
TemplateCategory: 'Category B',
},
]
I have the below code which produces the following undesired result:
result = [...data
.reduce((acc, {TemplateCategory, TemplateName, ID}) => {
const group = acc.get(TemplateCategory)
group ? group.options.push(ID, TemplateName) : acc.set(TemplateCategory, {TemplateCategory, "options":[ID, TemplateName]})
return acc
}, new Map)
.values()
]
console.log(result) // undesired result:
[
{
TemplateCategory: 'Category A',
options: [1, 'Template 1', 2, 'Template 2']
},
{
TemplateCategory: 'Category B',
options: [3, 'Template 3']
}
]
I am stuck on trying to convert options to an Array of Objects with value and label as properties. Also im struggling trying to reword TemplateCategory property to label.
My desired result is:
[
{
label: 'Category A',
options: [
{
value: 1,
label: 'Template 1'
},
{
value: 2,
label: 'Template 2'
}
]
},
{
label: 'Category B',
options: [
{
value: 3,
label: 'Template 3'
}
]
}
]
TIA
Like this
const data = [
{
ID: 1,
TemplateName: 'Template 1',
TemplateCategory: 'Category A',
},
{
ID: 2,
TemplateName: 'Template 2',
TemplateCategory: 'Category A',
},
{
ID: 3,
TemplateName: 'Template 3',
TemplateCategory: 'Category B',
},
]
const result = [...data
.reduce((acc, {TemplateCategory, TemplateName, ID}) => {
const group = acc.get(TemplateCategory)
group ? group.options.push({value: ID, label: TemplateName}) : acc.set(TemplateCategory, {label: TemplateCategory, "options":[{value: ID, label: TemplateName}]})
return acc
}, new Map)
.values()
]
console.log(result) // undesired result:
I want to convert an object from one format to another. So far my attempts at doing this recursively failed; either I'm getting a maximum stack exception or I'm unable to iterate over all paths.
Let's assume we have an object that lists questions and their answers. There may be N questions and M answers.
Object at start:
var before = {
item: 'Question 1',
id: '1',
type: 'Question',
items: [
{
item: 'Answer 1',
id: '1.1',
type: 'Answer',
items:
[
{
item: 'Question 2',
id: '1.1.1',
type: 'Question',
items: [
{
item: 'Answer 2.1',
id: '1.1.1.1',
type: 'Answer'
},
{
item: 'Answer 2.2',
id: '1.1.1.2',
type: 'Answer'
}
]
}
// ...
]
}, {
item: 'Answer 1',
id: '1.2',
type: 'Answer',
items:
[
{
item: 'Question 3',
id: '1.2.1',
type: 'Question',
items: [
{
item: 'Answer 3.1',
id: '1.2.1.1',
type: 'Answer'
},
{
item: 'Answer 3.2',
id: '1.2.1.2',
type: 'Answer'
}
]
}
// ...
]
}
// ...
]
}
Object how it should look like (wrap all in 'items' array; change key names 'item' to 'title', 'id' to 'key', remove 'type', add 'color' depending on 'type'):
var after = {
items: [
{
title: 'Question 1',
key: '1',
color: 'Red',
items: [
{
title: 'Answer 1',
key: '1.1',
color: 'Blue',
items:
[
{
title: 'Question 2',
key: '1.1.1',
color: 'Red',
items: [
{
title: 'Answer 2.1',
key: '1.1.1.1',
color: 'Blue'
},
{
title: 'Answer 2.2',
key: '1.1.1.2',
color: 'Blue'
}
]
}
// ...
]
}, {
title: 'Answer 1',
key: '1.2',
color: 'Blue',
items:
[
{
title: 'Question 3',
key: '1.2.1',
color: 'Red',
items: [
{
title: 'Answer 3.1',
key: '1.2.1.1',
color: 'Blue'
},
{
title: 'Answer 3.2',
key: '1.2.1.2',
color: 'Blue'
}
]
}
// ...
]
}
// ...
]
}
]
}
It seems easy enough, but I can't get it to work. This is how I tried to iterate:
function iter(o) {
for(let k in o) {
if (!(['item', 'items', 'id'].includes(k))) // My object contains a few more keys I don't want to go down further into
continue
if (o[k] !== null && typeof o[k] === 'object') {
iter(o[k]); // Max stack exception
return;
}
}
};
Thank you very much!
You could map the objects and rename the keys and map nested items.
const
iter = ({ item: title, id: key, type, items, ...o }) => ({
title,
key,
color: 'color' + key,
...o,
...(items && { items: items.map(iter) })
}),
before = { item: 'Question 1', id: '1', type: 'Question', items: [{ item: 'Answer 1', id: '1.1', type: 'Answer', items: [{ item: 'Question 2', id: '1.1.1', type: 'Question', items: [{ item: 'Answer 2.1', id: '1.1.1.1', type: 'Answer' }, { item: 'Answer 2.2', id: '1.1.1.2', type: 'Answer' }] }] }, { item: 'Answer 1', id: '1.2', type: 'Answer', items: [{ item: 'Question 3', id: '1.2.1', type: 'Question', items: [{ item: 'Answer 3.1', id: '1.2.1.1', type: 'Answer' }, { item: 'Answer 3.2', id: '1.2.1.2', type: 'Answer' }] }] }] },
result = [before].map(iter);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can achieve this using map, I wrote an simple test to show my point here
this is the important part of the code
function rename(item: any) {
return {
title: item.item,
key: item.id,
color: item.type === 'Question' ? 'red' : 'blue',
items: item.items?.map(rename)
}
}
console.log(items.map(rename))
Of course if you're using typescript, change any to the appropriate type and pay attention that I'm using ? operator which will not work with javascript, so you could do something like
...
items: item.items ? item.items.map(rename) : undefined
...
I have the following array named filters and try to filter it by selected items.
At the end I want to have all filters where a selected item is, but only with the selected items
let filters = [
{
id: 0,
name: 'property',
items: [
{
id: 0,
name: 'x',
isSelected: false
},
{
id: 1,
name: 'y',
isSelected: true
}
]
},
{
id: 1,
name: 'property2',
items: [
{
id: 0,
name: 'x',
isSelected: true
},
{
id: 1,
name: 'y',
isSelected: false
}
]
}
]
I want to get the following array at the end:
let filteredFilters = [
{
id: 0,
name: 'property',
items: [
{
id: 1,
name: 'y',
isSelected: true
}
]
},
{
id: 1,
name: 'property2',
items: [
{
id: 0,
name: 'x',
isSelected: true
}
]
}
]
I tried the following code, but it does not work.
let filteredFilters = filters.filter(filter => {
return filter.items.filter(item => {
return item.isSelected === true;
})
})
You need map + filter since you're dealing with a nested array:
let filters = [
{
id: 0,
name: 'property',
items: [
{
id: 0,
name: 'x',
isSelected: false
},
{
id: 1,
name: 'y',
isSelected: true
}
]
},
{
id: 1,
name: 'property2',
items: [
{
id: 0,
name: 'x',
isSelected: true
},
{
id: 1,
name: 'y',
isSelected: false
}
]
}
]
let filteredFilters =
filters.map(
({items, ...rest}) => ({...rest, items: items.filter(item => item.isSelected)})
)
.filter(x => x.items.length > 0);
console.log(filteredFilters);
I have two array of objects:
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
I need to create an array of strings that contains:
1. The Name of the first Book in books (if there are any)
2. The Names of the first 2 Cars in cars (if there are any)
I can do:
if (books.length > 0)
var bookA = books[0].name;
or:
if (cars.length > 1) {
var carA = cars[0].name;
var carB = cars[1].name;
}
Then build the string array but I believe there might be a better way to do this.
Can use filter() and map()
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
var res = [books[0], cars[0], cars[1]]
.filter(e => e)// remove undefined's
.map(({name:n}) => n)
console.log(res)
If you are using ES6. You can use [...array1,...array2] to merge them. So I slice the first item in book and use map to get a new array with only string name, and map it to result array.
For the cars array I slice the first two cars and do the same
var resultArray =[];
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
resultArray = [...resultArray, ...books.slice(0,1).map(v => v.name)]
resultArray = [...resultArray, ...cars.slice(0,2).map(v => v.name)]
console.log(resultArray)
One of a million ways to do it. This one would allow you to easily create a data structure (arrayDefinition) that configures what property to get from which array and at which index, which you could e.g. retrieve from a RESTful webservice.
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
const arrayDefinition = [
{
source: 'books',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 1,
prop: 'name'
}
];
let resultArr = []
arrayDefinition.forEach(function(def) {
if (Array.isArray(window[def.source]) && window[def.source].length >= def.index) {
resultArr.push(window[def.source][def.index][def.prop])
}
})
console.log(resultArr)