I am trying to add a new object using the map, but the new object is not inserted, i think there is an issue with my logic,
What I am trying to is I have some results data, I am trying to structure it, the English 1st part will be an object and English 2nd part will be another object, these 2 objects will be inside children, like the example
[
{
title: "English",
children: [
0:{
title: "1ST",
children: [
{
title: "CQ",
dataIndex: "CQ",
},
{
title: "MCQ",
dataIndex: "MCQ",
},
],
},
1:{
title: "2ND",
children: [
{
title: "CQ",
dataIndex: "CQ",
},
{
title: "MCQ",
dataIndex: "MCQ",
},
],
}
]
Here is my code, the English 2nd part is not inserting
const results = [
{ Field: "english_1st_CQ" },
{ Field: "english_1st_MCQ" },
{ Field: "english_2nd_CQ" },
{ Field: "english_2nd_MCQ" },
];
const structureData = (results) => {
let arr = [];
results.map((value) => {
if (value.Field.includes("_")) {
const [subjectName, subjectPart, suffix] = value.Field.split("_");
const isSubjectExist = arr.find((el) => el.title == subjectName);
if (isSubjectExist !== undefined) {
isSubjectExist.children.map((element, i) => {
if (element.title == subjectPart) {
element = {
title: subjectPart,
children: [
{
title: suffix,
dataIndex: suffix,
},
],
};
} else {
element.children["2"] = {
title: suffix,
dataIndex: suffix,
};
}
});
} else {
const subject = {
title: subjectName,
children: [
{
title: subjectPart,
children: [
{
title: suffix,
dataIndex: suffix,
},
],
},
],
};
arr.push(subject);
}
}
});
return arr;
};
console.log(structureData(results));
The issue is here:
if (element.title == subjectPart) {
element = {
title: subjectPart,
children: [
{
title: suffix,
dataIndex: suffix,
},
],
};
I think that using a functional approach helps to better keep track of and reason about what is happening during the iterations because it keeps things DRY:
function resolveNode (arr, title) {
const node = arr.find(node => node.title === title) ?? {title, children: []};
if (!arr.includes(node)) arr.push(node);
return node;
}
function parse (input) {
const result = [];
for (const {Field} of input) {
const [subject, part, suffix] = Field.split('_');
const s = resolveNode(result, subject);
const p = resolveNode(s.children, part);
// Don't create duplicates:
if (!p.children.find(node => node.title === suffix)) {
p.children.push({title: suffix, dataIndex: suffix});
}
}
return result;
}
const input = [
{ Field: "english_1st_CQ" },
{ Field: "english_1st_MCQ" },
{ Field: "english_2nd_CQ" },
{ Field: "english_2nd_MCQ" },
];
const result = parse(input);
const formattedJson = JSON.stringify(result, null, 2);
console.log(formattedJson);
Related
I am trying to split up an array of components into individual arrays depending on if there is a list breaking the pattern of heading, paragraph or hr. Here is what i have tried:
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
const richText = [];
const list = [];
components.forEach((component, index) => {
switch (component.name) {
case 'heading': case 'hr': case 'paragraph':
richText.push(component.name)
break
case 'list':
list.push(`${component.name}-index`)
break
}
})
console.log(richText)
Current output is:
[
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: ‘hr’ },
{ name: 'paragraph' }
]
Desired output is:
[
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' }
],
[
{ name: ‘hr’ },
{ name: 'paragraph' }
]
Considering list as a break, you can make initialization for richText as [[]]. You can keep pushing to last index of richText until a list is found. If a list is found push an empty array [] to your richText array.
Working Fiddle
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
const richText = [[]];
const list = [];
components.forEach((component, index) => {
switch (component.name) {
case 'heading': case 'hr': case 'paragraph':
richText[richText.length - 1].push(component);
break
case 'list':
list.push({name: `${component.name}-${richText.length}`})
index < components.length - 1 ? richText.push([]) : {};
break
}
})
console.log(richText);
console.log(list);
Same logic Array.reduce implementation
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
const richText = components.reduce((acc, curr, index) => {
if(curr.name === 'list' && index < components.length - 1) {
acc.push([]);
} else {
acc[acc.length - 1].push(curr);
}
return acc;
}, [[]]);
console.log(richText);
How about
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
let richText = [];
let list = [];
components.forEach((component, index) => {
switch (component.name) {
case 'heading': case 'hr': case 'paragraph':
richText.push({'name': component.name})
break
case 'list':
list.push(richText);
richText = [];
break
}
})
console.log(list);
Here's a generic function akin to python's groupby. Given an iterable and a key function, it yields pairs [key, chunk] where each chunk contains sequential items that have the same key:
function* groupBy(iter, keyFn) {
let last = null, buf = []
for (let item of iter) {
let key = keyFn(item)
if (key !== last) {
if (buf.length)
yield [last, buf]
buf = []
}
buf.push(item)
last = key
}
yield [last, buf]
}
Applied to your problem:
for (let [isList, tags] of groupBy(components, c => c.name === 'list'))
if (isList) ... else ...
Example of the data (useContext)
data: {
projects:{
'project-1':{
name: 'project-1',
columnIds:['column-1', 'column-2'],
},
'project-2':{
name: 'project-2',
columnIds:['column-3'],
},
},
columns:{
'column-1':{
title: 'column-1',
content: 'abc',
},
'column-2':{
title: 'column-2',
content: 'def',
},
'column-3':{
title: 'column-3',
content: 'ghi',
},
},
}
If I delete the project-1 which has column-1 and column-2, it should also be deleted inside the columns object. So the result should be:
data: {
projects:{
'project-2':{
name: 'project-2',
columnIds:['column-3'],
},
},
columns:{
'column-3':{
title: 'column-3',
content: 'ghi',
},
},
}
I already know how to remove project-1 object but I'm stuck in removing the column-1 and column-2.
This was my code for removing column-1 and `column-2 data:
let newColumn = data.projects[projectname].columnIds.map((item) =>
Object.keys(data.columns).filter((key) => key !== item)
);
You just need to check if there are corresponding column IDs before you delete the property. If there are, iterate over the IDs and delete the columns:
const data = {
projects: {
'project-1': {
name: 'project-1',
columnIds: ['column-1', 'column-2'],
},
'project-2': {
name: 'project-2',
columnIds: ['column-3'],
},
},
columns: {
'column-1': {
title: 'column-1',
content: 'abc',
},
'column-2': {
title: 'column-2',
content: 'def',
},
'column-3': {
title: 'column-3',
content: 'ghi',
},
},
}
const key = 'project-1'
const { columnIds } = data.projects[key]
if (columnIds.length > 0) {
columnIds.forEach(id => {
delete data.columns[id]
})
}
delete data.projects[key]
console.log(data)
You can also create a copy of the object so you don't mutate the original one:
const data = {
projects: {
'project-1': {
name: 'project-1',
columnIds: ['column-1', 'column-2'],
},
'project-2': {
name: 'project-2',
columnIds: ['column-3'],
},
},
columns: {
'column-1': {
title: 'column-1',
content: 'abc',
},
'column-2': {
title: 'column-2',
content: 'def',
},
'column-3': {
title: 'column-3',
content: 'ghi',
},
},
}
function deleteColumns(obj, key) {
const copy = {
projects: { ...obj.projects },
columns: { ...obj.columns }
}
const { columnIds } = copy.projects[key]
if (columnIds.length > 0) {
columnIds.forEach(id => delete copy.columns[id])
}
delete copy.projects[key]
return copy
}
const afterDelete = deleteColumns(data, 'project-1')
console.log(afterDelete)
console.log(data)
I have an example array
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
I'm looking to modify the array to:
const modifiedArray = [
{ obj: [{ title: 'titleValue1' }, { title: 'titleValue2' }] },
{ obj: [{ title: 'titleValue3' }, { title: 'titleValue4' }] },
]
So when I loop over the modified array I can call 'obj.title' instead of 'obj.fields.title'
I think this can be achieved using .map. So far I have tried:
const ammendedArray = array.map(item => ({ ...item, title: item.map(e => e.fields) }))
But returning 'item.map is not a function'
Any help with this would be much appreciated.
In your code you are trying to use map for an item in the top level array. Which is like this for the first item,
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] }
As you can see item is an object. You can not map through an object. What you can do is map through item.obj
const ammendedArray = array.map(item => ({ ...item, title: item.obj.map(e => e.fields) }))
But it will not solve your problem you will get a wrong array of objects like this,
[
{
obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }],
title: [{ title: 'titleValue1' }, { title: 'titleValue2' }]
},
...
]
You will have to update the obj key instead. What you need to do is the following,
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
const res = array.map((item) => {
return {
obj: item.obj.map(i => {
return i.fields
})
};
});
console.log(res);
I could reach to that like this :)
const array = [
{ obj: [{ fields: { title: 'titleValue1' } }, { fields: { title: 'titleValue2' } }] },
{ obj: [{ fields: { title: 'titleValue3' } }, { fields: { title: 'titleValue4' } }] },
]
// pass a function to map
const map1 = array.map((x)=>{
const filedsArray = [...x.obj]
x.obj = []
filedsArray.forEach((y)=>{
x.obj.push({title:y.fields.title})
})
return x
})
console.log(map1);
I have an array I need to get all the tasks that have the same record Id in an array
workspaces=[{recordId:1,tasks:[{title:'me'},{title:'we'}]},{recordId:2,tasks:[{title:'hi'},{title:'it'}]},{recordId:1,tasks:[{title:'they',{title:'she'}]}]
the final result will be like:[[recordId:1,tasks:[{title:'me'},{title:'we'},{title:'they',{title:'she'}]],[recordId:2,tasks:[{title:'hi'},{title:'it'}]]]
i used groupBy from lodash but i did get a separate arrays anyone have any idea how to implement that.
A possible solution could be a two step approach by
collecting items for a certain group
render the array in the wanted format.
This approach features a Map and uses Array.from for getting the wanted result.
var workspaces = [{ recordId: 1, tasks: [{ title: 'me' }, { title: 'we' }] }, { recordId: 2, tasks: [{ title: 'hi' }, { title: 'it' }] }, { recordId: 1, tasks: [{ title: 'they' }, { title: 'she' }] }],
grouped = Array.from(
workspaces.reduce((m, { recordId, tasks }) =>
m.set(recordId, [...(m.get(recordId) || []), ...tasks]), new Map),
([recordId, tasks]) => ({ recordId, tasks })
);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz solution is more sophisticated, but harder to read.
You can achieve the same like this:
const workspaces= [
{recordId: 1, tasks: [{title:'me'}, {title:'we'}] },
{recordId: 2, tasks: [{title:'hi'}, {title:'it'}] },
{recordId: 1, tasks: [{title:'they'}, {title:'she'}] }
]
const workspacesById = []
workspaces.forEach(w => {
const idx = workspacesById.findIndex(item => item.recordId === w.recordId)
if (idx > -1) {
workspacesById[idx].tasks = [...workspacesById[idx].tasks, ...w.tasks]
} else {
workspacesById.push(w)
}
})
console.log(workspacesById)
I think this is close to what you want
const arr = [
{ recordId: 1, tasks: [{ title: 'me' }, { title: 'we' }] },
{ recordId: 2, tasks: [{ title: 'hi' }, { title: 'it' }] },
{ recordId: 2, tasks: [{ title: 'f' }, { title: 'e' }] },
{ recordId: 2, tasks: [{ title: 'hi' }, { title: 'it' }] },
];
const result = arr.reduce((result, item) => {
if (result[item.recordId]) {
const prevTasks = result[item.recordId];
result[item.recordId].tasks = prevTasks.concat(item.tasks);
} else {
result[item.recordId] = item.tasks;
}
return result;
}, {});
The result will be
{
"1": [
{
"title": "me"
},
{
"title": "we"
}
],
"2": [
{
"title": "hi"
},
{
"title": "it"
}
]
}
Here is your result
let result = [];
for(let i=0; i<workspaces.length; i++) {
let recordFound = false;
if (result.length > 0) {
for(let j=0; j<result.length ; j++) {
if(workspaces[i].recordId === result[j].recordId) {
result[j].tasks = [...result[j].tasks, ...workspaces[i].tasks];
recordFound = true;
}
}
}
if (!recordFound) {
result.push(workspaces[i]);
}
}
console.log(result); //your expected result
i want to push my retrieved results which is an inline string result to my array object item
Here is my code :
arrayObject.push({ type: "type", item: itemArray });
arrayObject.forEach((elementItem) => {
global.forEach((element) => {
const { items } = element;
for (const item in items) {
const title = items[item].title;
elementItem.item.push({ title });
}
});
});
And here is my json file that i retrieve from global, items and title
global: [
{
items: {
xxx: {
title: 'result1',
}
},
}
]
The result i want is like this :
[ { type: 'xxx', item: [ {name: result1 } ] } ]
Here i've used reduce and object.values to produce your expected outcome.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const global = [{
way: 'type1',
items: {
get: {
title: 'result1',
},
post: {
title: 'result2',
},
put: {
title: 'result3',
},
},
},
{
way: 'type2',
items: {
get: {
title: 'test1',
},
post: {
title: 'test2',
},
put: {
title: 'test3',
},
},
},
]
function mapJsonToTypes(arr) {
const typeAndTitles = (acc, {items, way: type}) => {
return [...acc, {type, item: getTitlesFromItems(items)}]
}
return arr.reduce(typeAndTitles, []);
}
function getTitlesFromItems(items = {}) {
return Object.values(items).map(({ title }) => title)
}
console.log(mapJsonToTypes(global));