Was wondering if anyone knows of a way to use lodash, or vanilla JS to achieve this small problem?
I have this starting object:
{
"1": {
"null": {
"2": {
"3": {
"6": {
"7": "c"
},
"null": {
"null": {
"5": "b"
}
}
}
}
}
},
"8": {
"10": "e",
"null": {
"9": "d"
}
}
}
Each level (horizontally) means something. So level 1 is of type A, level 2 is of type B, 3 of type A, 4 of type B and so forth. So it alternates.
Is there a nice and simply way to "collapse" this object to look something like this:
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "A",
"label": "2",
"children": [
{
"type": "B",
"label": "3",
"children": [
{
"type": "A",
"label": "6",
"children": [
{
"type": "A",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
In essence annotating each level with what type it is, and nesting its children.
Here is the code
function transformObj(obj, level) {
level = level || 1;
var result = _(obj).transform(function(result, value, key) {
var obj = {
type: (level % 2 === 0) ? 'B' : 'A',
label: key
};
if (key === 'null') {
result.push(transformObj(value, level+1));
} else {
if (_.isObject(value)) {
obj.children = transformObj(value, level+1);
} else {
obj.value = value;
}
result.push(obj);
}
}, [])
.flatten()
.value();
return result;
}
Here is the output
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "A",
"label": "2",
"children": [
{
"type": "B",
"label": "3",
"children": [
{
"type": "A",
"label": "6",
"children": [
{
"type": "B",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
This should do the trick:
var source = {
"1": {
"null": {
"2": {
"3": {
"6": {
"7": "c"
},
"null": {
"null": {
"5": "b"
}
}
}
}
}
},
"8": {
"10": "e",
"null": {
"9": "d"
}
}
};
function collapse(obj, parent, level){
var result = parent || [];
level = level || 0;
for(prop in obj){
var item = obj[prop];
var build = {
type : level % 2 ? "B" : "A",
label : prop
//, level : level
}
if(typeof item == 'object'){
build.children = [];
collapse(item, build.children, level + 1);
} else {
build.value = item;
}
result.push(build);
}
return result;
}
var output = collapse(source);
var result = JSON.stringify(output, null, ' ');
console.log(result);
var elem = document.getElementById("result");
elem.innerHTML = result;
<pre id="result"></pre>
function doIt(data){
return _.chain(data)
.transform(function(result, value, key){
if(key !== 'null'){
var type = _.parseInt(key) % 2 === 1 ? 'A' : 'B';
if(_.isObject(value) && !_.includes(_.keys(value), 'prop1')){
result.push({
type: type,
label: key,
children: doIt(value)
});
} else {
result.push({
type: type,
label: key,
value: value
});
}
} else {
if(_.isObject(value)){
result.push(doIt(value));
}
}
}, [])
.flatten()
.value();
}
var result = doIt(data);
result is
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "B",
"label": "2",
"children": [
{
"type": "A",
"label": "3",
"children": [
{
"type": "B",
"label": "6",
"children": [
{
"type": "A",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
},
{
"type": "B",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
Related
Currently i have below Array of Objects
const dataClass = [
{
"id": 101,
"class": [
{
"type": "A",
"value": "A-class"
},
{
"type": "B",
"value": "B-class"
},
{
"type": "C",
"value": "C-class"
}
],
"rank": 1
},
{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
},
{
"id": 103,
"class": [
{
"type": "G",
"value": "G-class"
},
{
"type": "H",
"value": "H-class"
},
{
"type": "I",
"value": "I-class"
}
],
"rank": 3
}
];
i need to get dataClass object using all value inside the class object, let say i want to get the second object, so i have to search/input "type": "D", "type": "E", and "type": "F".
return array object/object i expect:
[{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
}]
I don't find any solution so far, Thanks for any help.
I added one more object with types D, E, F at rank 4
If you want to return all objects that match your filtration, check result1
and if you just wanna return the first object that matches, check result2
const dataClass = [
{
"id": 101,
"class": [
{
"type": "A",
"value": "A-class"
},
{
"type": "B",
"value": "B-class"
},
{
"type": "C",
"value": "C-class"
}
],
"rank": 1
},
{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
},
{
"id": 103,
"class": [
{
"type": "G",
"value": "G-class"
},
{
"type": "H",
"value": "H-class"
},
{
"type": "I",
"value": "I-class"
}
],
"rank": 3
},
{
"id": 104,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 4
}
];
const expectedValues = ['D', 'E', 'F'];
//use this if you wanna return all objects that match expectedValues
const result1 = dataClass.filter(el => el.class.every(obj => expectedValues.includes(obj.type)));
console.log('all matched Objects => ', result1);
//use this if you wanna return the first object that match expectedValues
const result2 = dataClass.find(el => el.class.every(obj => expectedValues.includes(obj.type)));
console.log('first matched object => ',result2);
Hope this will help,
const dataClass = [
{
"id": 101,
"class": [
{
"type": "A",
"value": "A-class"
},
{
"type": "B",
"value": "B-class"
},
{
"type": "C",
"value": "C-class"
}
],
"rank": 1
},
{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
},
{
"id": 103,
"class": [
{
"type": "G",
"value": "G-class"
},
{
"type": "H",
"value": "H-class"
},
{
"type": "I",
"value": "I-class"
}
],
"rank": 3
}
];
const resultArr = [];
for (const ch_arr of dataClass){
for (const class_arr of ch_arr["class"]){
if(["D","E","F"].includes(class_arr["type"])){
resultArr.push(ch_arr);
break;
}
};
};
// resultArr is the expected array
You need find the object inside of class Array so i think using find method is the more readable way to solved it
function findClassByType(value: string) {
return [dataClass.find((obj) => obj.class.find(({ type }) => type.toLocaleLowerCase() === value.toLocaleLowerCase()))];
}
console.log(findClassByType('a'))
I added the toLocaleLowerCase to avoid case sensitive.
My array look like this:
var theset=[
{
"set": "1",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "111", "check": false },
{ "field": "C", "value": "111", "check": true }
]
},
{
"set": "2",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "222", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "3",
"data": [
{ "field": "A", "value": "333", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "4",
"data": [
{ "field": "A", "value": "444", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "444", "check": true }
]
}
];
What I want to do is validate the "value" with other set on the same field only when the "check" is true.
The result is to return a true if there is a duplication of "value" in the set. The example will return a true because
set 1: having duplicate value for field A with set 2
set 2: having duplicate value for field A with set 1, duplicate value for field C with set 3
set 3: having duplicate value for field C with set 2
set 4: not consider as duplicate, even though the value of field B match with set 3 because of the check is false
so far I tried to do for loop on the list but this will have a lot of nested loop which is not efficient.
for(var i=0; i<theset.length; i++){
var checking = theset[i].data;
for(var j=0; j<checking.length; j++){
if(checking[j].check){
for(var k=0; k<theset.length; k++){
if(k!=i){
var checking2 = theset[k].data;
for(var l=0; l<checking2.length; l++){
...
}
}
}
}
}
}
Can anybody help me?
one way can be for each set to filter the duplicate data with array.filter
then if there is duplicate do the wanted treatment
var theset = [
{
"set": "1",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "111", "check": false },
{ "field": "C", "value": "111", "check": true }
]
},
{
"set": "2",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "222", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "3",
"data": [
{ "field": "A", "value": "333", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "4",
"data": [
{ "field": "A", "value": "444", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "444", "check": true }
]
}
];
theset.forEach(set => {
var duplicate = set.data.filter(data => {
return theset.some(oneSet => oneSet.data.some(oneData => oneData.value === data.value));
});
if (duplicate.length) {
console.log(`the following set have duplicate ${set.set}`);
console.log(duplicate);
//treat as you want the set and the duplicate
}
});
i would do it with a hash map
const theset = [
{
"set": "1",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "111", "check": false },
{ "field": "C", "value": "111", "check": true }
]
},
{
"set": "2",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "222", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "3",
"data": [
{ "field": "A", "value": "333", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "4",
"data": [
{ "field": "A", "value": "444", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "444", "check": true }
]
}
];
const hashmap = {};
theset.forEach((item) => {
item.data.forEach((obj) => {
if (!obj.check) {
return;
}
const key = JSON.stringify(obj);
if (!hashmap[key]) {
hashmap[key] = [];
}
hashmap[key].push(item.set);
})
})
console.log('hashmap', hashmap)
as a result you would get the following output:
{
"obj1": ["1","2"],
"obj2": ["1"],
"obj3": ["2","3"],
"obj4": ["3"],
"obj5": ["4"],
"obj6": ["4"]
}
obj1 is common for set 1 and set 2
obj3 is common for set 2 and set 3
you have the all needed information for your conclusions
of course, you could use a custom hash function rather than JSON.stringify()
I have an object that I would like to filter and only return the objects where salesPersonID = "1"
var jsonData = {
"a": {
"id": "a",
"name": "Lucifer Enterprises",
"salesPersonId": "1"
},
"b": {
"id": "b",
"name": "Charlies Chocolate Factory",
"salesPersonId": "1"
},
"c": {
"id": "c",
"name": "Geckos Investments",
"salesPersonId": "2"
}
};
Expected output:
var jsonDataFiltered = {
"a": {
"id": "a",
"name": "Lucifer Enterprises",
"salesPersonId": "1"
},
"b": {
"id": "b",
"name": "Charlies Chocolate Factory",
"salesPersonId": "1"
}
};
What I have tried
Using filter directly on the object which results in Uncaught TypeError: jsonData.filter is not a function
var filteredJsonData = jsonData.filter(function (row){
console.log("test");
});
Using Object entries and filter which returns a and b but with a different structure that will be an issue for what I am using the object for down the line.
var filteredJsonData = Object.entries(jsonData).filter(function (entry){
return entry[1].salesPersonId == "1";
});
Output from test 2 which has the right values but the wrong structure:
[
[ "a", { "id": "a", "name": "Lucifer Enterprises", "salesPersonId": "1" } ],
[ "b", { "id": "b", "name": "Charlies Chocolate Factory", "salesPersonId": "1" } ]
]
The question
How can I get the desired output?
You can use Object.fromEntries:
var jsonData = {
"a": {
"id": "a",
"name": "Lucifer Enterprises",
"salesPersonId": "1"
},
"b": {
"id": "b",
"name": "Charlies Chocolate Factory",
"salesPersonId": "1"
},
"c": {
"id": "c",
"name": "Geckos Investments",
"salesPersonId": "2"
}
};
var filteredJsonData = Object.fromEntries(Object.entries(jsonData).filter(function (entry){
return entry[1].salesPersonId == "1";
}));
console.log(filteredJsonData);
I want to write a function that will find the Categoy[3].options[3].label and matches it to the letter "D".
How do I iterate through the nested objects below to go through the TagCategory.options and search for the option that matches to the letter "D"? If it matches the letter "D", it should return true.
"Category":
[
{
"field": "A",
"options": [
{"tag": "100", "value": "yes"}
],
"label": "Red"
},
{
"field": "tanks",
"type": true,
"options": [
{"tag_value": "4", "value": "4", "label": "A"},
{"tag_value": "3", "value": "3", "label": "B"},
{"tag_value": "2", "value": "2", "label": "C"},
{"tag_value": "1", "value": "1", "label": "D"},
{"tag_value": "5", "value": "5", "label": "E"}
],
"label": "Tanks"
}
]
Something like this?
function findLabel(arr, label) {
for (var i in arr) {
const options = arr[i].options;
const find = options.find(o => o.label == label);
if (find) {
return true;
}
}
return false;
}
const test = findLabel(Category, "D");
Category is your array
function check(category, letter){
var obj;
var optionObj;
for(var i = 0; i < category.length; i++){
obj = Category[i];
options = obj.options;
for(var j = 0; j < options.length; j++){
optionObj = option[j];
if(optionObj.label === letter) return true;
}
}
return false;
}
Here is a rough example of what I mean by a nested for loop.
category = {...}; // your object above
for (i in category) {
field = category[i];
for (option_i in field.options) {
// This is the nested for loop - called thus because it's inside one.
if (field.options[option_i].label == "D"){
return true;
}
}
}
You can use Array.prototype.some()
let optionHasLabelDBool = obj.Category.some(({options}) =>
options.some(({label}) => label === "D"));
let obj = {
"Category":
[{
"field": "A",
"options": [{
"tag": "100",
"value": "yes"
}],
"label": "Red"
},
{
"field": "tanks",
"type": true,
"options": [{
"tag_value": "4",
"value": "4",
"label": "A"
}, {
"tag_value": "3",
"value": "3",
"label": "B"
}, {
"tag_value": "2",
"value": "2",
"label": "C"
}, {
"tag_value": "1",
"value": "1",
"label": "D"
}, {
"tag_value": "5",
"value": "5",
"label": "E"
}],
"label": "Tanks"
}
]
};
let optionHasLabelD = obj.Category.some(({options}) => options.some(({label}) => label === "D"));
console.log(optionHasLabelD);
Using a function, without arrow function or object destructing
function checkOption(array, prop, key, value) {
return array.some(function(obj) {
return obj[prop].some(function(match) {
return match[key] === value;
});
});
}
let optionHasLabelDBool = checkOption(obj.Category, "options", "label", "D");
let obj = {
"Category": [{
"field": "A",
"options": [{
"tag": "100",
"value": "yes"
}],
"label": "Red"
},
{
"field": "tanks",
"type": true,
"options": [{
"tag_value": "4",
"value": "4",
"label": "A"
}, {
"tag_value": "3",
"value": "3",
"label": "B"
}, {
"tag_value": "2",
"value": "2",
"label": "C"
}, {
"tag_value": "1",
"value": "1",
"label": "D"
}, {
"tag_value": "5",
"value": "5",
"label": "E"
}],
"label": "Tanks"
}
]
};
function checkOption(array, prop, key, value) {
return array.some(function(obj) {
return obj[prop].some(function(match) {
console.log(match[key], value, match[key] === value);
return match[key] === value;
});
});
}
let optionHasLabelDBool = checkOption(obj.Category, "options", "label", "D");
console.log(optionHasLabelDBool);
[
{
"id": "a",
"pid": "a",
"name": "AA",
},
{
"id": "b",
"pid": "a",
"name": "BB",
},
{
"id": "c",
"pid": "a",
"name": "CC",
},
{
"id": "x",
"pid": "b",
"name": "XX",
}
]
Above is the data I got from the database. Every person has an id and a pid, pid points to the person's higher level person's id. If a person has highest level, the id equals pid.
I want to convert the raw data to hierarchical JSON, like this:
[
{
"id": "a",
"name": "AA",
"child": [
{
"id": "b",
"name": "BB"
"child": [
{
"id": "x",
"name": "XX"
}
]
},
{
"id": "c",
"name": "CC"
}
]
}
]
I'm using Node.js.
I suggest you to create a tree and take id === pid as a root for the tree, which works for unsorted data.
How it works:
Basically, for every object in the array, it takes the id for building a new object as parentid for a new object.
For example:
{ "id": 6, "pid": 4 }
It generates this property first with id:
"6": {
"id": 6,
"pid": 4
}
and then with pid:
"4": {
"children": [
{
"id": 6,
"pid": 4
}
]
},
and while all objects are similarly treated, we finally get a tree.
If id === pid, the root node is found. This is the object for the later return.
var data = [
{ "id": "f", "pid": "b", "name": "F" },
{ "id": "e", "pid": "c", "name": "E" },
{ "id": "d", "pid": "c", "name": "D" },
{ "id": "c", "pid": "b", "name": "C" },
{ "id": "a", "pid": "a", "name": "A" },
{ "id": "b", "pid": "a", "name": "B" }
],
tree = function (data) {
var r, o = Object.create(null);
data.forEach(function (a) {
a.children = o[a.id] && o[a.id].children;
o[a.id] = a;
if (a.id === a.pid) {
r = a;
} else {
o[a.pid] = o[a.pid] || {};
o[a.pid].children = o[a.pid].children || [];
o[a.pid].children.push(a);
}
});
return r;
}(data);
console.log(tree);
Influenced by the answer of Nina, this is my resolution just for the record.
function corrugate(data){
var root = "";
return data.reduce((t,o) => {
o.id === o.pid && (root = o.id);
t[o.id] ? t[o.id].name = o.name
: t[o.id] = {id: o.id, name: o.name};
t[o.pid] ? o.pid !== o.id ? t[o.pid].children.push(t[o.id])
: t[o.pid].children = t[o.pid].children || []
: t[o.pid] = {id: o.pid, children: [t[o.id]]};
return t;
},{})[root];
}
var data = [{ "id": "f", "pid": "b", "name": "F" },
{ "id": "e", "pid": "c", "name": "E" },
{ "id": "b", "pid": "a", "name": "B" },
{ "id": "d", "pid": "c", "name": "D" },
{ "id": "c", "pid": "b", "name": "C" },
{ "id": "a", "pid": "a", "name": "A" }
];
console.log(corrugate(data));