Return last value with recursion - Javascript - 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);

Related

Being able to remove duplicate keys from an array of objects

I have a question about how I can delete the existing elements, for example, in my case "Tallas" is repeated, could you please help me? Thank you very much to those who are willing to help me to solve this problem
const data =
[ { atributos: { Tallas: [{ id: 0, name: 'XS' }, { id: 1, name: 'S' }] }}
, { atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
The idea is to have this json format with the last "Tallas" since it is the last one that I added through my dynamic form.
const expected =
[{ atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
How do I do this is there a way to do it, I've tried with filter plus the findindex but I can't get to eliminate the repetition of the json res= new.filter((arr, index, self) => index === self.findIndex( (t) => (t.attributes === arr.attributes )))
To unique the array of objects, we can use the Javascript Set module, if the array has complex nested objects, we can stringify each object before creating new Set data. this below function will unique the array of complex objects.
function unique_array(array = []) {
const newSetData = new Set(array.map((e) => JSON.stringify(e)));
return Array.from(newSetData).map((e) => JSON.parse(e));
}
this is a function that takes an array and return the same array but delete every duplicated item
function removeDuplicates(arr) {
return arr.filter((item,
index) => arr.indexOf(item) === index);
}
I didn't understant the part written in spanish so I hope this is what you are looking for
This is a solution specific to your question. this is not a generic solution.
const data = [
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
{
atributos: {
Calzado: [
{ id: 0, name: "10" },
{ id: 1, name: "9.5" },
],
},
},
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
];
function uniqueArray(array) {
const resultObject = array.reduce((acc, eachValue) => {
let keys = Object.keys(eachValue.atributos);
keys.forEach((eachKey) => {
if (!acc[eachKey]) {
acc[eachKey] = [];
}
let list = eachValue["atributos"][eachKey].map(
(each) => each.id + "-" + each.name
);
acc[eachKey].push(...list);
});
return acc;
}, {});
const resultArray = Object.keys(resultObject).reduce((acc, each) => {
let setData = Array.from(new Set(resultObject[each]));
acc.push({
atributos: {
[each]: setData.map((e) => {
return { id: e.split("-")[0], name: e.split("-")[1] };
}),
},
});
return acc;
}, []);
return resultArray;
}
const result = uniqueArray(data)
console.log("result ", JSON.stringify(result, null, 2));

Advanced filter object in js

I'm trying filter Object of Arrays with Object but i don't have idea what can I do it.
Sample:
{
245: [
{
id: "12",
name: "test",
status: "new"
},
{
id: "15",
name: "test2",
status: "old"
},
{
id: "12",
name: "test2",
status: "old"
}],
2815: [
{
id: "19",
name: "test",
status: "new"
},
{
id: "50",
name: "test2",
status: "old"
},
{
id: "120",
name: "test2",
status: "new"
}]
}
Need filter if status = "new" but struct must not change:
{
245: [{
id: "12",
name: "test",
status: "new"
}],
2815: [{
id: "19",
name: "test",
status: "new"
},
{
id: "120",
name: "test2",
status: "new"
}]
}
Loop over entries and create a new object with filtered values
const obj = {
245:[
{id:"12",name:"test",status:"new"},{id:"15",name:"test2",status:"old"},{id:"12",name:"test2",status:"old"}],
2815:[
{id:"19",name:"test",status:"new"},{id:"50",name:"test2",status:"old"},{id:"120",name:"test2",status:"new"}]
}
console.log(filter(obj, item => item.status === "new"))
function filter(obj, pred) {
return Object.fromEntries(Object.entries(obj).map(([name, value]) => [name, value.filter(pred)]))
}
You need to map over the object keys and then over the array elements to filter out the final result
var obj = {
245:[
{id:"12",name:"test",status:"new"},{id:"15",name:"test2",status:"old"},{id:"12",name:"test2",status:"old"}],
2815:[
{id:"19",name:"test",status:"new"},{id:"50",name:"test2",status:"old"},{id:"120",name:"test2",status:"new"}]
}
var res = Object.entries(obj).reduce((acc, [key, value]) => {
acc[key] = value.filter(item => item.status === "new");
return acc;
}, {})
console.log(res);
you can do it for this specific case like this:
const myObj = {
245:[
{id:"12",name:"test",status:"new"},
{id:"15",name:"test2",status:"old"},
{id:"12",name:"test2",status:"old"}
],
2815:[
{id:"19",name:"test",status:"new"},
{id:"50",name:"test2",status:"old"},
{id:"120",name:"test2",status:"new"}
]
};
const filteredObj = filterMyObj(myObj);
console.log(filteredObj);
function filterMyObj(myObj){
const myObjCopy = {...myObj};
for (const key in myObjCopy){
const myArrCopy = [...myObjCopy[key]];
myObjCopy[key] = myArrCopy.filter(item => item.status == "new");
}
return myObjCopy;
}
You can do it with filter :
for(let key in obj){
obj[key] = obj[key].filter(el => el.status == "new")
}

looping into nested array and modify data

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);

count occurrences of two keys in objects in array

I have the following array with objects and used the following code to creating a tally with the key "id":
var arr=[
{ id: 123,
title: "name1",
status: "FAILED"
},
{
id: 123,
title: "name1",
status: "PASSED"
},
{
id: 123,
title: "name1",
status: "PASSED"
},
{
id: 123,
title: "name1",
status: "PASSED"
},
{
id: 1234,
title: "name2",
status: "FAILED"
},
{
id: 1234,
title: "name2",
status: "PASSED"
}
];
const test =arr.reduce((tally, item) => {
if (!tally[item.id]) {
tally[item.id] = 1;
} else {
tally[item.id] = tally[item.id] + 1;
}
return tally;
}, {});
console.log(test);
Now what I want to do is to modify the tally to take in consideration the key status as well so the result will be somthing like:
[
{id:123, status:"PASSED", tally:3},
{id:123, status:"FAILED", tally:1},
{id:1234, status:"PASSED", tally:1},
{id:1234, status:"FAILED", tally:1}
]
Any idea? Thanks!
Just make the key item.id + item.status, then it's a simple assignment
const res = Object.values(arr.reduce((a, b) => {
a[b.id + b.status] = Object.assign(b, {tally: (a[b.id + b.status] || {tally: 0}).tally + 1});
return a;
}, {}));
console.log(res);
<script>
const arr=[
{ id: 123,
title: "name1",
status: "FAILED"
},
{
id: 123,
title: "name1",
status: "PASSED"
},
{
id: 123,
title: "name1",
status: "PASSED"
},
{
id: 123,
title: "name1",
status: "PASSED"
},
{
id: 1234,
title: "name2",
status: "FAILED"
},
{
id: 1234,
title: "name2",
status: "PASSED"
}
];
</script>
here you go
const test = arr.reduce((acc, item) => {
let found = acc.find(obj => obj.id === item.id && obj.status === item.status)
if (typeof found === "undefined") {
item.tally = 1
acc.push(item);
} else {
found.tally++;
}
return acc;
}, []);
You should group your items first using key that will contain both id and status:
const result = arr.reduce((acc, item) => {
const key = item.id + item.status;
acc[key] = acc[key] || { ...item, tally: 0 };
acc[key].tally++;
return acc;
}, {});
console.log( Object.values(result) );
Output:
[
{ id: 123, title: 'name1', status: 'FAILED', tally: 1 },
{ id: 123, title: 'name1', status: 'PASSED', tally: 3 },
{ id: 1234, title: 'name2', status: 'FAILED', tally: 1 },
{ id: 1234, title: 'name2', status: 'PASSED', tally: 1 },
]
Simply create a key with combination of id and status. And make a map using it. After that you can simply get the desired result from that.
Try the following:
var arr=[{id:123,title:"name1",status:"FAILED"},{id:123,title:"name1",status:"PASSED"},{id:123,title:"name1",status:"PASSED"},{id:123,title:"name1",status:"PASSED"},{id:1234,title:"name2",status:"FAILED"},{id:1234,title:"name2",status:"PASSED"}];
const map =arr.reduce((tally, item) => {
tally[item.id+"_"+item.status] = (tally[item.id+"_"+item.status] || 0) +1;
return tally;
}, {});
const result = Object.keys(map).map((a)=>{
var obj = {
id : a.split("_")[0],
status : a.split("_")[1],
tally : map[a]
};
return obj;
});
console.log(result);

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 }) ))
);

Categories

Resources