I've got an object with multiple key holding an array of values. Inside that array, there is a nested value on which I want to group on, while maintaining the main key.
So in my data I've got:
{
"foo1": [
{
"name": "foo1",
"subpart": {
"part": "bar1"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar1"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar2"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar2"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar3"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar3"
}
}
],
"foo2": [
{
"name": "foo2",
"subpart": {
"part": "bar1"
}
},
{
"name": "foo2",
"subpart": {
"part": "bar1"
}
},
{
"name": "foo2",
"subpart": {
"part": "bar2"
}
},
{
"name": "foo2",
"subpart": {
"part": "bar3"
}
},
{
"name": "foo2",
"subpart": {
"part": "bar3"
}
}
]
}
and I want it to become:
{
"foo1": {
"bar1": [
{
"name": "foo1",
"subpart": {
"part": "bar1"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar1"
}
}
],
"bar2": [
{
"name": "foo1",
"subpart": {
"part": "bar2"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar2"
}
}
],
"bar3": [
{
"name": "foo1",
"subpart": {
"part": "bar3"
}
},
{
"name": "foo1",
"subpart": {
"part": "bar3"
}
}
]
},
"foo2": {
"bar1": [
{
"name": "foo2",
"subpart": {
"part": "bar1"
}
},
{
"name": "foo2",
"subpart": {
"part": "bar1"
}
}
],
"bar2": [
{
"name": "foo2",
"subpart": {
"part": "bar2"
}
}
],
"bar3": [
{
"name": "foo2",
"subpart": {
"part": "bar3"
}
},
{
"name": "foo2",
"subpart": {
"part": "bar3"
}
}
]
}
}
Right now I've found some examples, but that only does grouping on just one level, where they 'key' is the first group, but I've got a second property in some subvalue to be the second level of grouping (if that makes any sense).
Best I've got so far is:
function groupBy<T>(arr: T[], fn: (item: T) => any) {
return arr.reduce<Record<string, T[]>>((prev, curr) => {
const groupKey = fn(curr);
const group = prev[groupKey] || [];
group.push(curr);
return { ...prev, [groupKey]: group };
}, {});
}
and then:
const grouped = groupBy(data, (x) => x.subpart.part)
But that groups it into a main key which I don't want.
Essentially you need to map groupBy over each value in data.
Mapping over objects is somewhat difficult/not straight-forward in TypeScript. What you can do is using Object.entries and Object.fromEntries to convert the object to an array first, map it and convert it back to an object. doing this, however, breaks type inference and makes the result of type any. So be careful when you need to do this while maintaining type-safety.
You might also want to consider refactoring your code such that an array-based structure is used as input and/or output for this transformation, as its easier to work with, and easier to maintain type-safety with.
const data = { "foo1": [...], ...}
const dataArr = Object.entries(data);
const mappedArr = dataArr.map(([k,v]) => [k, groupBy(v, (x) => x.subpart.part)]);
const result = Object.fromEntries(mappedArr);
TS Playground
Related
Fairly new to this but can someone help me?
I have the following JSON:
{
"city": [
{
"city_description": {
"text": {
"st": "capital"
}
},
"city_land": {
"st": {
"st": "Other"
}
},
"city_size": {
"id": [
{
"id": "small"
},
{
"id": "big"
},
{
"id": "moderate"
}
]
},
"city_type": {
"id": [
{
"id": "1"
},
{
"id": "2"
},
{
"id": "3"
}
]
},
"conception_date": {
"st": {
"st": "13051977"
}
},
"mark_row": {
"id": {
"id": "1"
}
}
},
{
"city_description": {
"text": {
"st": "cottage"
}
},
"city_land": {
"st": {
"st": "Other"
}
},
"city_size": {
"id": [
{
"id": "small"
},
{
"id": "big"
},
{
"id": "moderate"
}
]
},
"city_type": {
"id": [
{
"id": "1"
},
{
"id": "2"
},
{
"id": "3"
}
]
},
"conception_date": {
"st": {
"st": "15071999"
}
},
"mark_row": {
"id": {
"id": "2"
}
}
}
],
"country": {
"country_code": {
"coordinates": {
"id": "00111022"
},
"name_of_country": {
"st": "Belarus"
},
"desc": {
"st": "Non-eu"
}
},
"country_identifier": {
"id": {
"id": "99"
}
},
"country_description": {
"st": {
"st": "TBD"
}
},
"country_type": {
"is": [
{
"is": "01"
},
{
"is": "X90"
}
]
},
"country_id": {
"si": {
"si": "3"
}
}
}
}
This is stored in snowflake as a string.
I am able to select the data (eg. first column) for the first array.
I am able to select the data (eg. first column) for the first array:
SELECT
f.VALUE:city_description:text:st AS city_description
FROM tableinsnowflake t,
LATERAL flatten(input => t.PARSED_DATA, path => 'city') f
I want to do the same for COUNTRY but seem somehow stuck. Any thoughts? Thanks!
The country could be accessed directly from parsed_data column without using FLATTEN:
SELECT
f.VALUE:city_description:text:st::TEXT AS city_description,
t.parsed_data:country:country_code:name_of_country:st::TEXT AS name_of_country
FROM tab t,
LATERAL FLATTEN(input => t.PARSED_DATA, path => 'city') f
Here i was trying merge elements from 2 child arrays in to its parent one and leave the other one and move the second array to two levels up.
Is there a way to change value on condition, like in the input below,
parties.party.sno ="1" , can this updated as parties..sno='Y'
input:
{
"Parties": [
{
"party": {
"partyId": "100005767",
"sno": 1,
"fn": "Th1mas",
"ln": "Edison",
"emails": [
{
"emailAddress": "jkjk#ui.com"
}
],
"addresses": [
{
"zip": ""
}
],
"shealth": [
{
"stcd": "TN",
"lno": "1"
}
]
},
"seq": {
"typeCd": "1"
}
}
]
}
Expected output:
{
"person": {
"first_name": "Th1mas",
"middle_initial": "Edison",
"last_name": "",
"email_address": "jkjk#ui.com",
"pinCode": ""
},
"shealth": {
"statecd": "ON"
},
//this is the seq no from party.sno
"primary": "Y",
"typeCd": "1"
}
tried spec like this:
[
{
"operation": "shift",
"spec": {
"Parties": {
"*": {
"party": {
"emails": {
"*": {
"emailAddress": "[&1].email_address",
"#(2,fn)": "[&1].first_name",
"#(2,ln)": "[&1].last_name"
}
},
"addresses": {
"*": {
"zip": "[&1].pinCode"
}
},
"shealth": {
"*": {
"stcd": "[&1].statecd"
}
}
}
}
}
}
}
]
This spec works,
"party": {
"sno": {
"1": {
"#Y": "primary"
}
},
Try applying the condition from the current level,
[
{
"operation": "shift",
"spec": {
"Parties": {
"*": {
"party": {
"sno": {
"1": {
"#Y": "primary"
}
},
"emails": {
"*": {
"emailAddress": "person.email_address",
"#(2,fn)": "person.first_name",
"#(2,ln)": "person.last_name"
}
},
"addresses": {
"*": {
"zip": "person.pinCode"
}
},
"shealth": {
"*": {
"stcd": "shealth.statecd"
}
}
},
"seq": {
"typeCd": "typeCd"
}
}
}
}
}
]
In the code below, I am trying to make an array and remove duplicates from array with reactjs:
The array called names is set in state:
this.state = {
names = []
}
How can I remove the duplicated names and place them into the array
const data = [
{
"obj": {
"no": "1",
"info": [
{
"name": "maya"
},
{
"name": "mina"
}
]
}
},
{
"obj": {
"no": "2",
"info": [
{
"name": "maya"
}
]
}
},
{
"obj": {
"no": "3",
"info": [
{
"name": "mina"
},
{
"name": "Mike"
}
]
}
}
]
data.map((elem) => {
for(let i = 0 ; i < elem.info.length;i++){
let name_info = elem.info[i].name
this.setState({
names: [...this.state.names, name_info]
})
}
})
expected output :["maya","mina",Mike]
If you're fan of one line
[...(new Set(data.map(d => d['obj']['info']).flat().map(info => info['name'])))]
Step by step explanation:
First map takes the input returns only info part of each entry:
data.map(d => d['obj']['info']) yields array of array containing info.
[[{ name: "maya" }, { name: "mina" }], [{ name: "maya" }], [{ name: "mina" }, { name: "Mike" }]]
flat() takes the input from previous map which is the array of array and yields array of elements, so it becomes
[{ name: "maya" }, { name: "mina" }, { name: "maya" }, { name: "mina" }, { name: "Mike" }]
map() takes the input from previous flat which is array of object (which contains name) and returns array of name value.
So you got [ "maya", "mina", "maya", "mina", "Mike" ]
The final array is given to Set, by definition set cannot contain same element more than one. Set of previous array is [ "maya", "mina", "Mike" ].
As final step, set is converted to the array by using spread operator.
const data = [
{
"obj": {
"no": "1",
"info": [
{
"name": "maya"
},
{
"name": "mina"
}
]
}
},
{
"obj": {
"no": "2",
"info": [
{
"name": "maya"
}
]
}
},
{
"obj": {
"no": "3",
"info": [
{
"name": "mina"
},
{
"name": "Mike"
}
]
}
}
];
let names = [];
data.forEach(item => {
Object.values(item)[0].info.forEach(person => {
if(names.indexOf(person.name) === -1)
{
names.push(person.name)
}
})
})
console.log(names);
I think this can help you
First, this is a helper function to get just the unique value of an array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
And, this is how can get the result you want
const newNames = data.map((elem) => elem.obj.info.map(info => info.name)).flat().filter(onlyUnique)
You can then use it like this
this.setState({
names: [...this.state.names, ...newNames]
})
const data = [{
"obj": {
"no": "1",
"info": [{
"name": "maya"
}, {
"name": "mina"
}]
}
}, {
"obj": {
"no": "2",
"info": [{
"name": "maya"
}]
}
}, {
"obj": {
"no": "3",
"info": [{
"name": "mina"
}, {
"name": "Mike"
}]
}
}]
const names = data.flatMap(obj => obj.obj.info.map(info => info.name));
const unique = names.filter((name, i) => names.indexOf(name) === i);
console.log(unique);
I have an object array and i am filtering it against property name "username" like this.
array = [{
"id": 1,
"username": "admin",
"roles": [{
"name": "Administrator"
},
{
"name": "agent"
}
]
},
{
"id": 2,
"username": "admin2",
"roles": [{
"name": "Administrator2"
},
{
"name": "agent2"
}
]
},
{
"id": 3,
"username": "admin3",
"roles": [{
"name": "Administrator3"
},
{
"name": "agent3"
}
]
}
]
and the filter function is like this
transform(array: any, valueToSearch: string): any[] {
return array.filter(e =>
e.username.toLowerCase().indexOf(valueToSearch.toLowerCase())
!== -1);
}
everything works fine, but now i want to filter against the property name "name" in "roles" array in the object. for example i would like to return an object whose "roles" array contains "name" = agent3 , so it should return the whole object which is located at the last in my example. i tried like
return agents.filter(e => e.roles.filter(ee =>
ee.valueToSearch.toLowerCase()) !== -1));
but it didn't work.
this is dmeo
https://stackblitz.com/edit/angular-txchxs?embed=1&file=src/app/agentFilter.pipe.ts
As per the example given by you in the question, i was able to change your existing function like this and i hope this is your requirement..
ngOnInit() {
this.transform(this.array,'agent3');
}
transform(array: any, valueToSearch: string): any[] {
return this.array.filter(e => {
e.roles.filter(ee => {
if(ee.name.toLowerCase() === valueToSearch.toLowerCase() ) {
console.log(e);
this.finalResult = e;
}
})
})
}
Working Stackblitz: https://stackblitz.com/edit/angular-uzgni7
myarray = [{
"id": 1,
"username": "admin",
"roles": [{
"name": "Administrator"
},
{
"name": "agent"
}
]
},
{
"id": 2,
"username": "admin2",
"roles": [{
"name": "Administrator2"
},
{
"name": "agent2"
}
]
},
{
"id": 3,
"username": "admin3",
"roles": [{
"name": "Administrator3"
},
{
"name": "agent3"
}
]
}
];
function myFunction(){
var filtered= myarray.filter((obj)=>{
return obj.username.match(new RegExp(document.getElementById('search').value,'ig'));
});
console.log(filtered);
};
<input type="text" id="search" onkeyup="myFunction()"/>
I've this entry:
"entries": {
"members": {
"person": [
{
"name": "Jane Doe",
}
]}}
Now I would like to check if the persons array is empty or has some entries.
I already tried with $exists:
"selector": {
"entries": {
"members": {
"person": {
"name": {
"$exists": true
}
}
}
}
}
}
And with $neq
"selector": {
"entries": {
"members": {
"person": {
"name": {
"$neq": ""
}
}
}
}
}
}
Both approaches don't work..any tips?
you may want try using the $size operator. for example,
"selector": {
"entries": {
"members": {
"person": {
"$size": 0
}
}
}
}
I did it with:
"entries.members.person": {
"$elemMatch": {
"name": {
"$exists": true
}
}
}