looping into nested array and modify data - javascript

As per the above structure, All the types are having text-1.
I wanted only the first occurrence to be in text-1 rest all should be of text-2 in the data structure.
And the order of the block should not be changed. Also block can have different types which should not get affected.
I tried to loop each block, but dono the logic to modify only first occurrence. Please help
Below is what i tried so far,
let newData = data.forEach(data => {
return data.block.forEach(block => {
return block.child.forEach((type, i) => {
if (i === 0) { return }
type.type = 'text-2'
})
});
});
Current Data
const data = [
{
block: [
{ child: [{ type: 'text-1' }] },
{ child: [{ type: 'any type' }] },
{ child: [{ type: 'text-1' }] }
]
},
{
block: [
{ child: [{ type: 'text-1' }] },
{ child: [{ type: 'text-1' }] }
]
}
]
Expected Data,
const data = [
{
block: [
{ child: [{ type: 'text-1' }] },
{ child: [{ type: 'any type' }] },
{ child: [{ type: 'text-2' }] }
]
},
{
block: [
{ child: [{ type: 'text-2' }] },
{ child: [{ type: 'text-2' }] }
]
}
]

You could map the items and the nested ones as well by taking a function for getting the first string once and for all other calls the other string.
const
getOnce = (first, other, used) => () => used ? other : (used = true, first);
data = [{ block: [{ child: [{ type: 'text-1' }] }, { child: [{ type: 'any type' }] }, { child: [{ type: 'text-1' }] }] }, { block: [{ child: [{ type: 'text-1' }] }, { child: [{ type: 'text-1' }] }] }],
type1 = 'text-1',
type2 = 'text-2',
getType = getOnce(type1, type2),
result = data.map(
({ block }) => ({
block: block.map(
({ child }) => ({
child: child.map(o => Object.assign({}, o, o.type === type1 && { type: getType() }))
})
)
})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Generic alternative using the JSON.parse revival parameter :
var found = 0, data = [{ block: [{ child: [{ type: 'text-1' }] }, { child: [{ type: 'any type' }] }, { child: [{ type: 'text-1' }] }] }, { block: [{ child: [{ type: 'text-1' }] }, { child: [{ type: 'text-1' }] }] }]
data = JSON.parse(JSON.stringify(data),
(key, val) => key == 'type' && val == 'text-1' && found++ ? 'text-2' : val)
console.log( data )
Mutating alternative with for...of :
const data = [{ block: [{ child: [{ type: 'text-1' }] }, { child: [{ type: 'any type' }] }, { child: [{ type: 'text-1' }] }] }, { block: [{ child: [{ type: 'text-1' }] }, { child: [{ type: 'text-1' }] }] }];
let found = 0;
for (const item of data)
for (const block of item.block)
for (const child of block.child)
if (child.type === 'text-1' && found++)
child.type = 'text-2';
console.log( data );

Assuming that:
The array structure is the one shown in the example.
every data element is an object.
every object has a block property and it's an array.
every block element might have a type property.
you want to alter the original array without making a new copy of it (that's what I've understood from the question, but I can be wrong, of course).
Here is an (ugly) solution to alter the values as required.
As a side note, this kind of alteration is weird and doesn't really seems to make much sense, I highly recommend you to consider using alternative data structures.
Code explanation is directly in the code below.
const data = [
{
block: [
{ child: [{ type: 'text-1' }] },
{ child: [{ type: 'any type' }] },
{ child: [{ type: 'text-1' }] }
]
},
{
block: [
{ child: [{ type: 'text-1' }] },
{ child: [{ type: 'text-1' }] }
]
}
];
// That's unreasonable.. anyway.
data.forEach((o, i) => {
// loop each element, keep track of the index.
if (i > 0) {
// if it's NOT the first element, get all the elements who actually have a child, loop the child and for each type property, replace 1 with 2.
o.block.forEach(c => c.child && c.child.forEach(child => child.type = child.type.replace("1","2")));
}
else {
// otherwise, if that's the first element, just find the last block that has a child and whose child equals 'text-1'.
const elems = o.block.filter(c => c.child && c.child.length && c.child.some(child => child.type === 'text-1'));
const found = elems && elems.length && elems[elems.length - 1];
// if found, replace the value.
if (found) found.child[0].type = found.child[0].type.replace("1","2");
}
});
console.log(data);

Related

Return last value with recursion - Javascript

Hi all I have following data:
const section = {
fileds: [
{ id: "some Id-1", type: "user-1" },
{
child: [
{ id: "some Id-2", type: "user-2" },
{ fileds: [{ id: "kxf5", status: "pending" }] },
{ fileds: [{ id: "ed5t", status: "done" }] }
]
},
{
child: [
{ id: "some Id-3", type: "teacher" },
{ fileds: [{ id: "ccfr", status: null }] },
{ fileds: [{ id: "kdpt8", status: "inProgress" }] }
]
}
]
};
and following code:
const getLastIds = (arr) =>
arr.flatMap((obj) => {
const arrayArrs = Object.values(obj).filter((v) => Array.isArray(v));
const arrayVals = Object.entries(obj)
.filter(([k, v]) => typeof v === "string" && k === "id")
.map(([k, v]) => v);
return [...arrayVals, ...arrayArrs.flatMap((arr) => getLastIds(arr))];
});
console.log(getLastIds(section.fileds));
// output is (7) ["some Id-1", "some Id-2", "kxf5", "ed5t", "some Id-3", "ccfr", "kdpt8"]
My code doing following, it printing in new array all ids.
It's working but I don't need all ids.
I need to return only last id in array and I should use recursion.
The output should be
(4) [" "kxf5", "ed5t", "ccfr", "kdpt8"]
P.S. here is my code in codesandbox
Is there a way to solve this problem with recursion? Please help to fix this.
You can do it with reduce.
function getLastIds (value) {
return value.reduce((prev, cur) => {
if (cur.id) {
return [ ...prev, cur.id ];
} else {
let key = ('child' in cur) ? 'child' : 'fileds';
return [ ...prev, ...getLastIds (cur[key]) ]
}
}, []);
}
You could check if a certain key exists and take this property for mapping id if status exists.
const
getValues = data => {
const array = Object.values(data).find(Array.isArray);
return array
? array.flatMap(getValues)
: 'status' in data ? data.id : [];
},
section = { fileds: [{ id: "some Id-1", type: "user-1" }, { child: [{ id: "some Id-2", type: "user-2" }, { fileds: [{ id: "kxf5", status: "pending" }] }, { fileds: [{ id: "ed5t", status: "done" }] }] }, { child: [{ id: "some Id-3", type: "teacher" }, { fileds: [{ id: "ccfr", status: null }] }, { fileds: [{ id: "kdpt8", status: "inProgress" }] }] }] },
result = getValues(section);
console.log(result);

How to find the length of particular type within object using javascript?

i have object like below,
Example 1
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children: [
{
type_1: {
id: ‘12’,
},
type: 'item1-type',
},
{
children: [
{
id: '1',
type: 'item2',
},
{
id: '2',
type:'item2',
},
{
id:'3',
type: 'item2',
},
]
type: 'item2-type',
},
{
children: [
{
id: '4',
type: 'item2',
},
{
id: '5',
type:'item2',
},
{
id:'6',
type: 'item2',
},
]
type: 'item2-type',
},
]
}
now i want to find the count of "item2" type within children array within children array again.
note that the outer children array can be empty array and the children array within children array may not be present. so the input can be of types like below
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children: [] //empty children array
}
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children:
[ //no children array within
{
type_1: {
id: ‘12’,
},
type: “item1-type”,
},
]
}
how can i find the count of type: "item2" within children array considering example1 input.
so the expected count is 6.
could someone help me with this. thanks. new to programming.
const findAllChildrenOfType = (obj, type) => {
let count = 0;
if (obj.type === type) count++;
if (obj.children) {
obj.children.forEach(child => {
const childCount = findAllChildrenOfType(child, type);
count += childCount;
})
}
return count;
}
console.log(findAllChildrenOfType(input, "item2"))

Setting array keys dynamically based on length

Given an array in this format:
[
[{
name: "name",
value: "My-name"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test.com"
},
{
name: "comment",
value: "my-comment"
}
],
[{
name: "name",
value: "My-name2"
},
{
name: "qty",
value: "3"
},
{
name: "url",
value: "test2.com"
}
],
[{
name: "name",
value: "My-name3"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test3.com"
},
{
name: "comment",
value: "my-comment3"
}
]
]
I'm looking to switch that to:
[
[
{ name: "My-name" },
{ qty: "1" },
{ url: "test.com" },
{ comment: "my-comment", }
],[
{ name: "My-name2" },
{ qty: "3" },
{ url: "test2.com",
],[
{ name: "My-name3", },
{ qty: "1", },
{ url: "test3.com", },
{ comment: "my-comment3", }
]
]
In other words, swapping out the array keys but maintaining the object structure within each array element.
I've tried looping over each element and can swap the keys out using something like:
newArray[iCount][item.name] = item.value;
However I'm then struggling to preserve the object order. Note that the comment field may or may not appear in the object.
With Array.map() function:
var arr = [
[{name:"name",value:"My-name"},{name:"qty",value:"1"},{name:"url",value:"test.com"},{name:"comment",value:"my-comment"}],
[{name:"name",value:"My-name2"},{name:"qty",value:"3"},{name:"url",value:"test2.com"}],
[{name:"name",value:"My-name3"},{name:"qty",value:"1"},{name:"url",value:"test3.com"},{name:"comment",value:"my-comment3"}]
],
result = arr.map(function(a){
return a.map(function(obj){
var o = {};
o[obj.name] = obj.value
return o;
});
});
console.log(result);
Check my moreBetterOutput value. I think will be better.
If you still need a result like your example in the question then you can check output value.
const input = [
[
{
name:"name",
value:"My-name"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test.com"
},
{
name:"comment",
value:"my-comment"
}
],
[
{
name:"name",
value:"My-name2"
},
{
name:"qty",
value:"3"
},
{
name:"url",
value:"test2.com"
}
],
[
{
name:"name",
value:"My-name3"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test3.com"
},
{
name:"comment",
value:"my-comment3"
}
]
]
const output = input.map(arr => arr.map(obj => ({[obj.name]: obj.value})))
const moreBetterOutput = output.map(arr => arr.reduce((acc, item, index) => {
acc[Object.keys(item)[0]] = item[Object.keys(item)[0]];
return acc;
}, {}) )
//console.log(output);
console.log(moreBetterOutput);
Another map function:
const result = array.map( subarray =>
Object.assign(...subarray.map( ({name, value}) => ({ [name] : value }) ))
);

How to find the first property that is an array in an object?

I'm creating a function that loops through an array like this:
schema: [{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}, {
And returns a callback with the values of the objects:
eachDeep (array, callback) {
array.forEach(item => {
item.fields.forEach(field => {
callback(field)
})
})
},
As you can see the item.fields.forEach part is harcoded. How can I modify the function so it detects the first property that it's an array and loop through it? (e.g. in this case that property is fields).
To find whether a property of an object is an array or not you can also use this one:
//let item be your object's property
if(typeof item == "object" && item.length > 0){
//do whatever if it is an array
}
You can check if the field is not an array or not, if so loop it, otherwise do something else with it.
var data = [{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}, {
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}];
eachDeep (array, callback) {
array.forEach(item => {
// loop through each property again
item.forEach(prop => {
// if property is an array
if (prop instanceof Array) {
prop.forEach(field => callback(field));
} else {
// property is not an array
// do something else
}
})
})
},
var big_array =
[
{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}
];
for (let item of big_array)
{
for (let key in item)
{
if (Array.isArray(item[key]) )
{
console.log('this is an array do something:', key);
}
}
}
You could check using Array.isArray()
If the goal is to find the first array property you can do the following. Using ES6.
const schema = [{
name: 'firstRow',
fields: [{
name: 'name',
text: 'Name',
type: 'text',
col: 12,
value: ''
}]
}]
let firstArr;
schema.forEach(item => {
firstArr = Object.keys(item).filter(k => Array.isArray(item[k]))[0];
})

How to delete particular nodes within a nested object tree in JavaScript

Here is where my algorithm skills ends. I can traverse through the object and find a certain object but I'm not able to delete the object in the same time.
Here is the object
const obj = {
children: [{
children: [
{
children: [
{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
}
],
key: 'root',
type: 'root_type'
},
{
key: 'error',
type: 'error_type'
}
]
}]
}
The object with the key === 'error' object can be in any children array. I want to find it and delete the object that contains the key.
The output should be like that:
let output = findAndDeleteObjByKeyAndType('error', 'error_type')
output = {
children: [{
children: [
{
children: [
{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
}
],
key: 'root',
type: 'root_type'
}
]
}]
}
Can someone help here?
Array methods like filter and every can come in handy here:
const object = {
children: [{
children: [{
children: [{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
},
{
key: 'error',
type: 'error_type'
}
],
key: 'root',
type: 'root_type'
},
{
key: 'error',
type: 'error_type'
}
]
}]
}
function purgeAll (object, target) {
if (object.children) {
const keys = Object.keys(target)
object.children = object.children.filter(o =>
!keys.every(k => target[k] === o[k]) && purgeAll(o, target)
)
}
return object
}
let output = purgeAll(object, {
key: 'error',
type: 'error_type'
})
console.log(output)
.as-console-wrapper { min-height: 100%; }

Categories

Resources