JSON extracted data returned too many times - javascript

I have a JSON with the following shape for ~50 _source entries:
{
"hits": [
{
"_source": {
"name": "henry",
"jobs": ["judge", "lawyer"]
}
},
{
"_source": {
"name": "henry",
"jobs": ["dev", "waitress"]
}
}
// ...
]
}
I want to extract only the first item in jobs node, i.e. judge, dev, ... and tried the following:
for (i in json.hits)
{
const jobExtract = json.hits[i]._source;
for (i=0; i<jobExtract.jobs.length; i++)
{
const firstItem = jobExtract.jobs[0];
console.log(firstItem);
}
}
I can extract the first item but it appears multiple times:
judge
judge
judge
dev
dev
Where have I gone wrong?

The first solution that pops up in my mind is using Reduce
Considering:
const hits = [
{
"_source": {
"name": "henry",
"jobs" : [ "judge","lawyer"] },
},
{
"_source": {
"name": "henry",
"jobs" : [ "dev","waitress"] },
}, ]
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], [])
console.log(result) // ["dev", "judge"]
Hope this helps

Related

Recursive function returns weird output?

So i have this problem where i need to fill a list with objects of children's ids,names and parents ids and names for further backend work.
When i select a child and I recursively fill the list it gives me this output
[
{
"_id": "6328354914a6c4002121c2c6",
"name": "sss21"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "ss21"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "s1"
},
{
"_id": "6328351214a6c4002121c2c5",
"name": "new"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "s1"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "ss21"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "sss21"
}
]
I use this function to fill the list
selectionOutput(node) {
const id = node._id;
const name = node.name;
if (node.parent !== null) {
this.selection.push({_id: id, name: name});
this.selectionOutput(node.parent);
}
this.selection.push({_id: id, name: name});
return this.selection;
}
The function expects a node with parent nodes which in this case is this one
{
"_id": "6328351214a6c4002121c2c5",
"name": "new"
}
the other 3 like "s1, ss21 and sss21" are its children.
How can i make the function return just
[
{
"_id": "6328351214a6c4002121c2c5",
"name": "new"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "s1"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "ss21"
},
{
"_id": "6328354914a6c4002121c2c6",
"name": "sss21"
}
]
I should probably add that my data structure looks like this:
{
"_id": "6328351214a6c4002121c2c5",
"name": "new"
"parent": null,
"children": {
"_id": "6328354914a6c4002121c2c6",
"name": "s1",
"parent": {"_id": "6328351214a6c4002121c2c5","name": "new"}
"children": {
"_id": "6328354914a6c4002121c2c6",
"name": "ss21",
"parent": {"_id": "6328354914a6c4002121c2c6","name": "s1"}
"children":{...} Hope you get the point
}
},...
}
Also my function is fed with and Object that is a child and it goes up a level from there. For example child3(parent: child2) -> child2(parent:child1) -> child1(parent: parent) -> parent. I want to map all the ids all the ancestors of the initial object including the id of the initial one.
Looking at your data structure you can try this approach:
data.reduce((accum, item) => {
const childs = [];
const getChilds = ({ _id, name, children}) => {
childs.push({ _id, name});
if (children) getChilds(children);
};
if (item.children) getChilds(item.children);
return [...accum, { _id: item._id, name: item.name, children: childs }];
}, [])
OR this one if you want all data at root level
data.reduce((accum, item) => {
const childs = [];
const getChilds = ({ _id, name, children}) => {
childs.push({ _id, name});
if (children) getChilds(children);
};
if (item.children) getChilds(item.children);
return [...accum, { _id: item._id, name: item.name }, ...childs];
}, [])
``
One push is all that the function needed. I moved the push before the if statement and it works fine now.
selectionOutput(node) {
const id = node._id;
const name = node.name;
this.selection.push({_id: id, name: name});
if (node.parent) {
this.selectionOutput(node.parent);
}
return this.selection;
}

Formatting JSON data for Tree Graph

I have a JSON array of the following format (this data is pulled from mongodb to be displayed as a tree graph on a react-based front-end):
[
{
"name": "7654321",
"children": [
{
"_id": "LjYgocn9PsHhEFbM7",
"accountId": "4343213"
},
{
"_id": "sB2ipCstYnLnHrAuu",
"accountId": "4343271"
},
{
"_id": "JhugmhxS7A57Y34wM",
"accountId": "4343276"
}
]
},
{
"name": "4343213",
"children": [
]
},
{
"name": "4343271",
"children": [
{
"_id": "sie9mtttgdRw7Ktma",
"accountId": "4343279"
}
]
},
{
"name": "4343279",
"children": [
{
"_id": "sie23mtttgdRw7Ktma",
"accountId": "8765345"
}
]
},
{
"name": "4343276",
"children": [
]
}
]
The goal is to re-format (rename and delete some keys) this data to be used in react-tree-graph. From the sample above, output should look like:
[
{
"name": "7654321",
"children": [
{
"name": "4343213"
},
{
"name": "4343271",
"children": [
{
"name": "4343279",
"children": [
{
"name": "8765345"
}
]
}
]
},
{
"name": "4343276"
}
]
}
]
Any help is appreciated!
You could first create a Map that has as keys the name property values, and as corresponding values the (unfinished) result objects. They start off with just the name property.
Then you can iterate the children information in the input, to wire the children into the above mentioned result objects, which can be done efficiently using the name as key in the Map.
Whenever you wire a child object into a parent object, you know that child is not a top-level object in the final result. So starting with all nodes, you would trim that list (a Set) all those nodes that occur in a children array. This will leave you with only the top level nodes, which in its array form represents the desired output.
Implementation:
let data = [{"name": "7654321","children": [{"_id": "LjYgocn9PsHhEFbM7","accountId": "4343213"},{"_id": "sB2ipCstYnLnHrAuu","accountId": "4343271"},{"_id": "JhugmhxS7A57Y34wM","accountId": "4343276"}]},{"name": "4343213","children": []},{"name": "4343271","children": [{"_id": "sie9mtttgdRw7Ktma","accountId": "4343279"}]},{"name": "4343279","children": [{"_id": "sie23mtttgdRw7Ktma","accountId": "8765345"}]},{"name": "4343276","children": []}];
let map = new Map(data.map(({name, children}) => [name, { name }]));
let roots = new Set(map.values());
for (let {name, children} of data) {
if (!children?.length) continue;
map.get(name).children = children.map(({accountId}) => {
let child = map.get(accountId) || { name: accountId };
roots.delete(child);
return child;
});
}
let result = Array.from(roots);
console.log(result);

Change structure of a JavaScript object

I have this object structure:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
I'd like to change it so it appears like this:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"suppliers":[
{
"supplier_id": 1,
"supplier_name": [
"Supplier1"
]
}
],
"supplier_code": "SUP001",
"count": "21"
}
}
I tried this hoping it would work:
const group = accumulator[item.id];
group.suppliers = [];
group.suppliers = group.suppliers.push(item.supplier_name, item.supplier_id, item.supplier_code);
return accumulator;
Unfortunately that just seems to give me a count of the objects pushed into suppliers, suppliers isn't an array and supplier_id, supplier_name and supplier_code are still visible outside of suppliers:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21",
"suppliers: 3
}
}
How do I change it to the format I want?
You could use es6 Destructuring assignment, Object.values es2017 (or Object.keys instead).
If you assume that users contains more then one user you could use reduce.
In the example below original object won't be mutated.
Hope it helps
const original = {
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
};
const { users } = original;
const reshaped = Object.values(users).reduce((acc, { id, supplier_id, supplier_name, ...rest }) => {
acc[id] = {
...rest,
suppliers: [{
supplier_id,
supplier_name: [supplier_name]
}]
};
return acc;
}, {});
console.log(reshaped);
You need to use an object to push into the suppliers array. Also, delete the old keys which are not needed.
Edit - You can directly create an array of 1 object. Thanks #Adam
const group = accumulator[item.id];
group.suppliers = [{
supplier_id: item.supplier_id,
supplier_name: item.supplier_name,
supplier_code: item.supplier_code
}];
delete group.supplier_id;
delete group.supplier_name;
delete group.supplier_code;
return accumulator;
Here's a quick and modern solution:
const parseUsers = (users) => {
let parsedUsers = {};
for (key in users) {
const user = users[key];
// destructuring (or extracting) the relevant keys from the . user object, keeping everything else under 'rest'
const { supplier_id, supplier_name, ...rest } = user;
parsedUsers[key] = {
...rest, // spreading back our rest params
suppliers: [ // creating a new array and populating it with the keys which we previously extracted (along with their corresponding values)
supplier_id,
supplier_name
]
}
}
return parsedUsers;
}
usage: parseUsers(json.users)

Group nested arrays in an object in js (Tree Vue.js)

This is my array of objects:
I am using vue.js , I need a tree like this to keep the structure of tree view: https://v2.vuejs.org/v2/examples/tree-view.html
[
{
"name": "",
"children": []
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": []
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2011",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Titoli",
"children": []
}
]
}
]
I need a function to retrive an object grouped by name with his childrens
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
},
{
"name": "2011",
"children": []
}
],
"name": "Titoli",
"children": []
}
]
}
I would like to know if there it is a simple way (instead of writing a recursive function), like using lodash or something near it.
Thanks
I think that i have implemented a more readable answer:
const rootTree = [];
const putInTree = (tree, node) => {
let nodeInTree = tree.find(x => x.name === node.name);
if (!nodeInTree) {
nodeInTree = {name: node.name, children: []};
tree.push(nodeInTree);
}
if (node.children[0]) putInTree(nodeInTree.children, node.children[0])
}
nodes.forEach(node => putInTree(rootTree, node));
nodes here is your start array, let me know if this is ok
treeArchive.forEach(element => {
element.children.forEach(father => {
if (result.children.length != 0) {
cicleChildrens(result, father);
function cicleChildrens(padrePrecedente, nuovoPadre){
var brother = padrePrecedente.children.find(x => x.name == nuovoPadre.name);
if (brother != undefined) cicleChildrens(brother, nuovoPadre.children[0]);
else padrePrecedente.children.push(nuovoPadre);
};
}
else result.children.push(father);
});
});
This is currently my working code.. I'm struggling tryng to understand your code #chriss
Try this one:
function getGroupedByName(given) {
let result = given.reduce((a, b) => {
if(!a[b.name]) a[b.name] = [];
a[b.name] = [...a[b.name], ...b.children];
return a;
}, {});
result = Object.keys(result).map(key => ({name: key, children: getByName(result[key])}));
return result;
}
const o = []; // your initial object
getGroupedByName(o, "Leggi")
It is returning it as an array of objects having name and children props, as i am assuming first level can also have multiple different names, not all being ""
It goes first trough all elements in array and groups them into object with structure { name: children } where children is array of all children for same group.
For each children array it preforms same operation, going trough array and flattening it into { name: children } object.
At this moment we have following structure:
{ "": {
Leggi: {...}
}}
When everything is grouped, Object.keys loops trough all keys and breaks it into array where key is name and value children property

how to return an array inside an array javascript

i have an array inside an array...how do i return all the values using a for loop in javascript/angular?
for example my json...
[
{
"Name": "element1",
"Attributes": [
{"file":"document.doc"},
{"file":"document2.doc"}
]
},
{
"Name": "element2",
"Attributes": [
{"file":"document3.doc"},
{"file":"document4.doc"}
]
},
{
"Name": "element3",
"Attributes": [
{"file":"document5.doc"},
{"file":"document6.doc"}
]
}
]
having a tough time just returning all the files within attributes...only seem to be getting the first one everytime.
EDIT:
what i have so far..
function getAllFiles() {
for (var i = 0; i < Attributes.file.length; i++) {
return Attributes.file[i];
}
}
One of the methods how to get the desired output, using Array#reduce.
var json = [{Name:"element1",Attributes:[{file:"document.doc"},{file:"document2.doc"}]},{Name:"element2",Attributes:[{file:"document3.doc"},{file:"document4.doc"}]},{Name:"element3",Attributes:[{file:"document5.doc"},{file:"document6.doc"}]}],
res = json.reduce(function(s,a){
s.push(...a.Attributes.map(c => c.file));
return s;
}, []);
console.log(res);
ES5
var json = [{Name:"element1",Attributes:[{file:"document.doc"},{file:"document2.doc"}]},{Name:"element2",Attributes:[{file:"document3.doc"},{file:"document4.doc"}]},{Name:"element3",Attributes:[{file:"document5.doc"},{file:"document6.doc"}]}],
res = json.reduce(function(s,a){
s = s.concat(a.Attributes.map(c => c.file));
return s;
}, []);
console.log(res);
try
var files = [];
json.forEach(function(obj) {
obj.Attributes.forEach(function (f) {
files.push(f.file); })
});
this loops on the json array then on each element's attributes then adds the vilue of file
You could show all the files using Array methods like map and reduce:
var data = [{
"Name": "element1",
"Attributes": [{
"file": "document.doc"
},
{
"file": "document2.doc"
}
]
},
{
"Name": "element2",
"Attributes": [{
"file": "document3.doc"
},
{
"file": "document4.doc"
}
]
},
{
"Name": "element3",
"Attributes": [{
"file": "document5.doc"
},
{
"file": "document6.doc"
}
]
}
];
var files = data.map(function (obj) {
return obj.Attributes.map(function (i) {
return i.file;
});
}).reduce(function (x, y) {
return x.concat(y);
});
console.log(files);
Although Kind user's answer is better:
var data = [{
"Name": "element1",
"Attributes": [{
"file": "document.doc"
},
{
"file": "document2.doc"
}
]
},
{
"Name": "element2",
"Attributes": [{
"file": "document3.doc"
},
{
"file": "document4.doc"
}
]
},
{
"Name": "element3",
"Attributes": [{
"file": "document5.doc"
},
{
"file": "document6.doc"
}
]
}
];
var files = data.reduce(function(acc, val) {
return acc.concat(
val.Attributes.map(function(attribute) {
return attribute.file;
})
);
}, []);
console.log(files);
I renamed some variables so that the code makes more sense (to me).
Something like outerArray.innerArray. in your case arrayName.Attributes should work.

Categories