AngularJS: Remove duplicate objects and reorder an array by value - javascript

I am using a json, this is the structure:
{ "variants": [
{ "capacity":"A", "color":"1" },
{ "capacity":"A", "color":"2" },
{ "capacity":"B", "color":"3" }
]};
Using angular.forEach or another method, I would like to display data no repeated, reordered it and related it by "capacity", something like this (a new array):
$scope.newArray = [
{ "capacity":"A", "color1":"1", "color2":"2" },
{ "capacity":"B", "color_1":"3" }
];
It is possible an angular way or simply javascript, I need help!

a = { 'variants': [
{'capacity': 'A','color': '1'},
{'capacity': 'A','color': '2'},
{'capacity': 'B','color': '3'},
]};
b = a.variants
c = b.reduce(function (prev, crt) {
prev[crt.capacity] = (prev[crt.capacity] || [
]).concat(crt.color)
return prev;
}, {})
// c is now {"A": ["1","2"], "B": ["3"]}
d = []
for (var name in c) {
if (c.hasOwnProperty(name)) {
f = {
'capacity': name
}
c[name].forEach(function (e) {
f['color' + e] = e;
})
// f is {"capacity": "A", "color1": "1","color2": "2"}
// and then {"capacity": "B", "color3": "3"}
d = d.concat(f)
}
}
// d is [{"capacity": "A", "color1": "1","color2": "2"},
// {"capacity": "B", "color3": "3"}]

Related

Create array of objects from sorting another array of objects

I want to create an array of objects, and the first elements from the each data object to be a separate object, the second element from the each data object to be another separate object and so on...
let array = [{
data: {
center1: "1",
storage1: "1",
system1: "1",
}
},
{
data: {
center2: "2",
storage2: "2",
system2: "2",
}
}
]
Expected result:
[
{ center1: "1", center2: "2"},
{ storage1: "1", storage2: "2"},
{ system1: "1", system2: "2"}
]
And this is what I tried do to but is not working really well:)
const rows = [];
array.forEach((item, index) => {
for (let key in item.data) {
rows.push({index : key + ': ' + item.data[key]});
}
});
The output is this:
[
{index : 'center1: 1'},
{index : 'storage1: 1'},
{index : 'system1: 1'},
{index : 'center2: 2'},
{index : 'storage2: 2'},
{index : 'system2: 2'}
]
Thank you for your help!
This'll be incredibly brittle, because key ordering is irrelevant to how JS objects work, and the assumption that "the first key in each object is the same kind of key" is really only that: an assumption. So, the first thing to fix would be to make all those objects use the same keys, not uniques, thus making "key ordering" irrelevant.
However, if that's not an option (and it almost certain is, but if it's not) then Object.entries will turn any object into a key/value array, which you can then use to restructure this data:
let array = [{
data: {
center1: "1",
storage1: "1",
system1: "1",
}
},
{
data: {
center2: "2",
storage2: "2",
system2: "2",
}
}
]
const restructured = array.reduce((result, e) => {
Object.entries(e.data).forEach(([key, val], pos) => {
if (!result[pos]) result[pos] = {};
result[pos][key] = val;
});
return result;
}, []);
console.log(restructured);
You can try something like this, which may probably optimized (of course it will work only if all objects strictly have the same structure) :
const array = [
{
data: {
center1 : "1",
storage1: "1",
system1 : "1",
}
},
{
data: {
center2 : "2",
storage2: "2",
system2 : "2",
}
}
];
const finalArray = [];
for (let i = 0; i < Object.keys(array[0].data).length; i++) {
finalArray.push({});
array.forEach(arrayItem => {
finalArray[i][Object.keys(arrayItem.data)[i]] = Object.values(arrayItem.data)[i]
});
}
console.log(finalArray)
let array = [{
data: {
center1: "1",
storage1: "1",
system1: "1",
}
},
{
data: {
center2: "2",
storage2: "2",
system2: "2",
}
}
]
const rows = [];
array.forEach(item => {
Object.keys(item.data).forEach( (dt, i) => {
if ( ! rows[i] ) {
rows.push({});
}
rows[i][dt] = item.data[dt];
});
});
console.log(rows)

Converting two lists to json

I'm attempting to convert two lists to json.
For example :
l1 = ['a','b','a']
l2 = ['q','r','s']
should be converted to :
[{
"name": "g",
"children": [{
"name": "a",
"children": [{
"name": "q"
}, {
"name": "s"
}]
},
{
"name": "b",
"children": [{
"name": "r"
}]
}
]
}]
Closest I have is :
l1 = ['a','b','a']
l2 = ['q','r','s']
nameDict = {}
childrenDict = {}
l1 = l1.map(x => {
return({name: x});
});
console.log(l1);
l2 = l2.map(x => {
return({children: x});
});
console.log(l2);
var c = l1.map(function(e, i) {
return [e, l2[i]];
});
console.log(JSON.stringify(c))
which produces :
[[{"name":"a"},{"children":"q"}],
[{"name":"b"},{"children":"r"}],
[{"name":"a"},{"children":"s"}]]
How to combine the elements produce ? :
[{
"name": "g",
"children": [{
"name": "a",
"children": [{
"name": "q"
}, {
"name": "s"
}]
},
{
"name": "b",
"children": [{
"name": "r"
}]
}
]
}]
Disclaimer: Since we don't know where the g comes from, I will only build the root children array.
Since your arrays have the same length, you can use a plain for and use with the index to play with both arrays. Just build an array and check each iteration if the "child" already exists. If not, create it.
l1 = ['a','b','a']
l2 = ['q','r','s']
let gChildren = []
for(let i = 0; i < l1.length; i++){
let group = gChildren.find(c => c.name === l1[i])
if(!group){
group = { name: l1[i], children: [] }
gChildren.push(group)
}
group.children.push({ name: l2[i] })
}
console.log(gChildren)
Here is working code that accounts for your pre-existing structure that accomplishes the result you are looking for.
let data1 = ["a","b","a"];
let data2 = ["q","r","s"];
let outputData = [{name: "g", children: []}];
for (let i=0;i < data1.length;i++) {
let found = false;
for (let j=0;j < outputData[0].children.length;j++) {
if (outputData[0].children[j].name === data1[i]) {
outputData[0].children[j].children.push({name: data2[i]});
found = true;
}
}
if (found === false) {
outputData[0].children.push({name: data1[i], children: [{name: data2[i]}]});
}
}
console.log(JSON.stringify(outputData));
This is a good use case for a Array.prototype.reduce, where you want to iterate over an array but end up with a single value.
l1.reduce((acc, val, i) => {
const l2Val = l2[i]
const foundObj = acc.find(o => o.name === val)
if (foundObj) {
foundObj.children.push({name: l2Val})
} else {
acc.push({
name: val,
children: [{name: l2Val}]
})
}
return acc
}, [])
Here, on each iteration I'm just adding the child to the children array for that item, or creating the value for the item if it doesn't already exist.
I have no idea what g corresponds to so I've left it out, but you can add the array created from reduce to another object or array if you want.
You could transpose the array and use the information as path to the final child object
l1 = ['a', 'b', 'a']
l2 = ['q', 'r', 's']
transposes to
[
['a', 'q'], // a -> q
['b', 'r'], // b -> r
['a', 's'] // a -> s
]
which is now works with reduce.
The advantage is to use it with longer pathes to the final children, like
[
['a', 'x', 'y', 'z'],
...
]
which returns a nested object with the given relation to each other.
const
transpose = array => array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []);
var l1 = ['a', 'b', 'a'],
l2 = ['q', 'r', 's'],
result = transpose([l1, l2]).reduce((r, a) => {
a.reduce((q, name) => {
var temp = (q.children = q.children || []).find(o => o.name === name);
if (!temp) q.children.push(temp = { name });
return temp
}, r);
return r;
}, { name: 'g' });
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A bit shorter with filter on the distinct keys :
var l1 = ['a','b','a'], l2 = ['q','r','s']
var children = [...new Set(l1)].map(k => ({ name: k, children:
l2.filter((v, i) => l1[i] == k).map(v => ({ name: v })) }))
console.log( [{ name: 'g', children }] )
Or more efficient with intermediate object of the groups :
var l1 = ['a','b','a'], l2 = ['q','r','s']
var children = Object.entries(
l1.reduce((o, k, i) => ((o[k] = o[k] || []).push(l2[i]), o), {})
).map(([k, v]) => ({ name: k, children: v.map(v => ({ name: v})) }))
console.log( [{ name: 'g', children }] )

Create a json array dynamically

I have an array of objects like this below,
var A = [
{
"111": ["A", "B", "C"]
},
{
"222": ["D", "E", "F"]
}
];
I would like to create a new array dynamically in the format as shown below using js or jQuery.
key in array A should be mapping to attribute text of AA and value should be to children as given below
var AA = [
{
"text": "111",
"state": "open",
"children": [
{
"text": "A"
},
{
"text": "B"
},
{
"text": "C"
}]
},
{
"text": "222",
"state": "open",
"children": [
{
"text": "D"
},
{
"text": "E"
},
{
"text": "F"
}]
}];
How can I accomplish this ? Any thoughts would be helpful
Thanks for all of your suggestions and help.
But Right now I would like to change variable A and input in the format below,
how can i accomplish the same as before.
var A = {"1":["1_1","1_2","1_3"],
"2":["2_1","2_2"],
"3":["3_1"],
"4":["4_1"],
"5":["5_1","5_2"]};
You can map A to AA using Array.prototype.map() (twice).
var AA = A.map(function(item) {
var key = Object.keys(item)[0]; // this will be reliable only because each item has one property.
return {
'text': key,
'state': 'open',
'children': item[key].map(function(child) {
return { 'text': child };
})
};
});
fiddle
My answer is too long, but its still understandable
var A = [
{"111":["A","B","C"]},
{"222":["D","E","F"]}
];
console.log(A);
A = superGenerator(A);
console.log(A);
function superGenerator(data){
var dataF = [];
var children = [];
for (var i = data.length - 1; i >= 0; i--) {
children[i] = [];
for (var [key, val] of iterate_object(data[i])) {
for (var j = val.length - 1; j >= 0; j--) {
children[i][j] = [];
children[i][j] = {
text : val[j]
}
};
}
dataF[i] = {
text : Object.keys(data[i])[0],
state : 'open',
children : children[i]
}
};
return dataF;
}
function* iterate_object(o) {
var keys = Object.keys(o);
for (var i=0; i<keys.length; i++) {
yield [keys[i], o[keys[i]]];
}
}
You can try it by click Run,
Cheers
Try this :
var A = [
{
"111": ["A", "B", "C"]
},
{
"222": ["D", "E", "F"]
}
];
var AA = A.map(item => {
return {
"text": Object.keys(item)[0],
"state": "open",
"children": item[Object.keys(item)[0]].map(elem => { return {"text": elem} })
}
});
console.log(AA);
You can use array#map to iterate through your array, for each object, iterate all its keys and generate the corresponding object.
var arr = [ { "111": ["A", "B", "C"] }, { "222": ["D", "E", "F"] } ],
result = arr.map(o => Object.keys(o).reduce((r,k) => {
return {
text : k,
state : 'open',
children : o[k].map(text => ({text}))
};
},{}));
console.log(result);

Grouping the elements of an object. (underscore.js)

I have an object as shown below :
[
{"ClientGroupName":"ABC","CompanyName":"AA","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"BB","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"CC","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"DD","ControlGroupName":"2"},
{"ClientGroupName":"ABC","CompanyName":"EE","ControlGroupName":"3"},
{"ClientGroupName":"DEF","CompanyName":"FF","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"GG","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"HH","ControlGroupName":"2"}
]
I need to group it like this :
[
[
[
{"ClientGroupName":"ABC","CompanyName":"AA","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"BB","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"CC","ControlGroupName":"1"}
],
[{"ClientGroupName":"ABC","CompanyName":"DD","ControlGroupName":"2"}],
[{"ClientGroupName":"ABC","CompanyName":"EE","ControlGroupName":"3"}]
],
[
[
{"ClientGroupName":"DEF","CompanyName":"FF","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"GG","ControlGroupName":"1"}
],
[{"ClientGroupName":"DEF","CompanyName":"HH","ControlGroupName":"2"}]
]
]
I am using underscore.js to group the elements in the object.
$scope.InitController = function () {
ClientGroupService.GetClientGroupList().then(function (response) {
$scope.groupByTwoFields = [];
$scope.groupByTwoFields = _.groupBy(response.data, function (obj) {
return obj.ClientGroupName + '|' + obj.ControlGroupName;
});
.....
});
};
The output from the above code looks like :
[
[
{"ClientGroupName":"ABC","CompanyName":"AA","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"BB","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"CC","ControlGroupName":"1"}
],
[{"ClientGroupName":"ABC","CompanyName":"DD","ControlGroupName":"2"}],
[{"ClientGroupName":"ABC","CompanyName":"EE","ControlGroupName":"3"}],
[
{"ClientGroupName":"DEF","CompanyName":"FF","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"GG","ControlGroupName":"1"}
],
[{"ClientGroupName":"DEF","CompanyName":"HH","ControlGroupName":"2"}]
]
What do I need to do in order to get the desired output as shown above.
Your code producing the output in the below shown form :
[
[
{"ClientGroupName":"ABC","CompanyName":"AA","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"BB","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"CC","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"DD","ControlGroupName":"2"},
{"ClientGroupName":"ABC","CompanyName":"EE","ControlGroupName":"3"}
],
[
{"ClientGroupName":"DEF","CompanyName":"FF","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"GG","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"HH","ControlGroupName":"2"}]
]
Here's a very simple function to do it in vanilla JavaScript, it takes two arguments:
arr The array containing the objects that you want to group.
properties An array of strings with the names of the properties you want to group the objects by, ordered by priority (objects will be ordered by the first property in the array, then the second, etc).
function groupByProperties(arr, properties) {
const groups = {
root: {
array: [],
children: {}
}
};
arr.forEach(obj => {
let group = groups.root;
properties.forEach(propertyKey => {
const property = obj[propertyKey];
if (!group.children.hasOwnProperty(property)) {
const child = {
array: [],
children: {}
}
group.array.push(child.array);
group.children[property] = child;
}
group = group.children[property];
});
group.array.push(obj);
});
return groups.root.array;
}
You would use it as follows:
let data = [
{"ClientGroupName":"ABC","CompanyName":"AA","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"BB","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"CC","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"DD","ControlGroupName":"2"},
{"ClientGroupName":"ABC","CompanyName":"EE","ControlGroupName":"3"},
{"ClientGroupName":"DEF","CompanyName":"FF","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"GG","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"HH","ControlGroupName":"2"}
];
console.log(groupByProperties(data, ["ClientGroupName", "ControlGroupName"]));
i did it with just vanillaJS in case the answer above didn't work for you:
var data = [
{"ClientGroupName":"ABC","CompanyName":"AA","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"BB","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"CC","ControlGroupName":"1"},
{"ClientGroupName":"ABC","CompanyName":"DD","ControlGroupName":"2"},
{"ClientGroupName":"ABC","CompanyName":"EE","ControlGroupName":"3"},
{"ClientGroupName":"DEF","CompanyName":"FF","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"GG","ControlGroupName":"1"},
{"ClientGroupName":"DEF","CompanyName":"HH","ControlGroupName":"2"}
];
var ClientGroupNames = [];
data.forEach(function(o){
if(ClientGroupNames.indexOf(o.ClientGroupName) < 0){
ClientGroupNames.push(o.ClientGroupName);
}
});
var result = ClientGroupNames.map(function(name){
return data.filter(function(comp){
return comp.ClientGroupName == name ? true : false;
})
}).map(function(grp){
var groupNames = [];
grp.forEach(function(company){
if(groupNames.indexOf(company.ControlGroupName) < 0)
groupNames.push(company.ControlGroupName);
})
return groupNames.map(function(name){
return grp.filter(function(gp){
return gp.ControlGroupName == name ? true : false;
})
})
})
_.groupBy doesn't return an array, it returns an object.
var data = [{
"ClientGroupName": "ABC",
"CompanyName": "AA",
"ControlGroupName": "1"
}, {
"ClientGroupName": "ABC",
"CompanyName": "BB",
"ControlGroupName": "1"
}, {
"ClientGroupName": "ABC",
"CompanyName": "CC",
"ControlGroupName": "1"
}, {
"ClientGroupName": "ABC",
"CompanyName": "DD",
"ControlGroupName": "2"
}, {
"ClientGroupName": "ABC",
"CompanyName": "EE",
"ControlGroupName": "3"
}, {
"ClientGroupName": "DEF",
"CompanyName": "FF",
"ControlGroupName": "1"
}, {
"ClientGroupName": "DEF",
"CompanyName": "GG",
"ControlGroupName": "1"
}, {
"ClientGroupName": "DEF",
"CompanyName": "HH",
"ControlGroupName": "2"
}];
var obj = _.groupBy(data,function (obj) {
return obj.ClientGroupName;
}); // groupBy returns an object, not a array
var result = Object.keys(obj).map(function (key) { return obj[key]; }); // this converts the object to an array
_.each(result,function(obj,index){ // loop through each item in the array
var _obj = _.groupBy(obj,function(obj2){
return obj2.ControlGroupName;
}); // group it by the ControlBroupName and convert it to a array
result[index] = Object.keys(_obj).map(function (key) { return _obj[key]; });
});
console.log("result:\n", result);
You have to groupBy twice:
result = _(data).groupBy('ClientGroupName').map(g =>
_.values(_.groupBy(g, 'ControlGroupName'))
).value()

Filtering array of objects with arrays based on nested value

I am trying to filter an array, based on some nested object. I prepared some Fiddle
Input array looks like this:
let arrayOfElements =
[
{
"name": "a",
"subElements":
[
{"surname": 1},
{"surname": 2}
]
},
{
"name": "b",
"subElements":
[
{"surname": 3},
{"surname": 1}
]
},
{
"name": "c",
"subElements":
[
{"surname": 2},
{"surname": 5}
]
}
];
I want the output for this case, to look like this:
let filteredArray =
[
{
"name": "a",
"subElements":
[
{"surname": 1}
]
},
{
"name": "b",
"subElements":
[
{"surname": 1}
]
}
];
I am using this formula to do that:
let filteredArray = arrayOfElements.filter((element) => element.subElements.some((subElement) => subElement.surname === 1));
Output is almost good, but it returns objects with all objects with surnames (better check that fiddle :D), instead of cutting them away. How can i improve the filtering ?
This way you can go as deep as you want in an array and filter elements at any level,
arrayOfElements.map((element) => {
return {...element, subElements: element.subElements.filter((subElement) => subElement.surname === 1)}
})
Spread operator will expand element and then filtered subElements will override the subElements in element.
After you call filter, you need to pipe the results to map, like this:
let filteredArray = arrayOfElements
.filter((element) =>
element.subElements.some((subElement) => subElement.surname === 1))
.map(element => {
let newElt = Object.assign({}, element); // copies element
return newElt.subElements.filter(subElement => subElement.surname === '1');
});
I am assuming here that you don't want to manipulate the original array. So, I am using Object.assign.
let filteredArray = arrayOfElements
.filter((element) =>
element.subElements.some((subElement) => subElement.surname == 1))
.map(element => {
return Object.assign({}, element, {subElements : element.subElements.filter(subElement => subElement.surname == 1)});
});
Just improved the answers above
let elements =
[
{
"name": "a",
"subElements":
[
{"surname": 1},
{"surname": 2}
]
},
{
"name": "b",
"subElements":
[
{"surname": 3},
{"surname": 1}
]
},
{
"name": "c",
"subElements":
[
{"surname": 2},
{"surname": 5}
]
}
];
var value = 1;
var filteredArray = elements
.filter(element => element.subElements
.some(subElement => subElement.surname === value)
)
.map(element => {
let n = Object.assign({}, element, {'subElements': element.subElements.filter(
subElement => subElement.surname === value
)})
return n;
})
console.log(filteredArray)
Try this solution:
data_filter = arrayOfElements.filter(function (element) {
return element.subElements.some( function (subElement) {
return subElement.surname === surname
});
});
You can make it generic as well:
Logic
Find all distinct surnames and loop over them
Filter every object to check if surnames exists. If yes, copy object using Object.assign and set subElements value to filtered list.
Create a temp array to hold all similar objects and push copied object to it.
Push this array to final array on every iteration of distinct surname.
Sample
let arrayOfElements=[{name:"a",subElements:[{surname:1},{surname:2}]},{name:"b",subElements:[{surname:3},{surname:1}]},{name:"c",subElements:[{surname:2},{surname:5}]}];
let distinct_surnames = [];
arrayOfElements.forEach(function(el) {
el.subElements.forEach(function(s) {
if (distinct_surnames.indexOf(s.surname) < 0) distinct_surnames.push(s.surname)
});
})
let result = [];
distinct_surnames.forEach(function(sn) {
let inter = [];
arrayOfElements.forEach(function(el) {
let f = el.subElements.filter(function(sub) {
return sub.surname === sn;
});
if (f.length > 0) {
let _tmp = Object.assign({}, el);
_tmp.subElements = f;
inter.push(_tmp);
}
});
result.push(inter);
})
console.log(result)
Note: Arrow functions are used to keep the reference of this. If you are not using this inside function, you can use normal functions as well.
function display_message() {
let arrayOfElements = [{
"name": "a",
"subElements": [{
"surname": 1
}, {
"surname": 2
}]
}, {
"name": "b",
"subElements": [{
"surname": 3
}, {
"surname": 1
}]
}, {
"name": "c",
"subElements": [{
"surname": 2
}, {
"surname": 5
}]
}];
// console.log(arrayOfElements);
var surname = 1;
let filteredArray = arrayOfElements.filter((element) => element.subElements.some((subElement) => subElement.surname === surname));
for(var data in filteredArray){
filteredArray[data].subElements = {"surname": surname};
}
console.log(filteredArray);
}
<input type="button" onclick="display_message();" value="click"/>
let filteredArray = arrayOfElements
.filter((element) =>
element.subElements.some((subElement) => subElement.surname === 1))
.map(element => {
let newElt = Object.assign({}, element); // copies element
newElt.subElements = newElt.subElements.filter(subElement => subElement.surName === '1');
return newElt;
});
is more correctly

Categories