Merging contents of an array based on matching content - javascript

I am writing some Google Apps Script to get each Google Group and list it's members then output it to a Google Sheet. Currently, I am grabbing each group and email address and pushing it to the memberArr, however I then want to 'merge' the relevant information.
So if for example, I have X Groups and Group 1 has 4 members (Foo, Bar, Baz and Quux) - Currently, it will output as
[ [ 'Group 1', 'Foo' ],
[ 'Group 1', 'Bar' ],
[ 'Group 1', 'Baz' ],
[ 'Group 1', 'Quux' ] ]
But I want to have it output as [Group 1, Foo, Bar, Baz, Quux].
That is to say, merge the contents of the memberArr where there is a common Group
Here is my code so far:
function getAllGroupsTEST() {
const groupArr = [];
let gPageToken;
let gPage;
do {
gPage = AdminDirectory.Groups.list({
customer: "my_customer",
maxResults: 100,
gPageToken
});
const groups = gPage.groups;
if (groups) {
groups.forEach(({email}) => {
const groupEmail = email;
groupArr.push(groupEmail);
});
}
gPageToken = gPage.nextPageToken;
} while (gPageToken);
console.log(`LOGGING GROUPS:\n\n${groupArr}`);
const memberArr = [];
let mPageToken;
let mPage;
groupArr.forEach(group => {
mPage = AdminDirectory.Members.list(group,{
customer: "my_customer",
maxResults: 500,
mPageToken
})
const members = mPage.members;
if (members) {
// console.log(`LOGGING ${members.length} MEMBERS FOR ${group}\n\n${members}`)
members.forEach(member => {
// console.log(`MEMBER: ${member.email} IS IN GROUP ${group}`)
memberArr.push([group, member.email])
})
}
})
// console.log(`COUNTED ${groupArr.length} GROUPS`)
// console.log(memberArr)
}
Any help would be greatly appreciated!
Edit: Updated to show the memberArr as is rather than in a table format.

I'd propose to convert the array in the object and then back to the array this way:
var arr = [
['group1','a'],
['group1','b'],
['group2','c'],
['group2','d'],
['group3','e']
]
var obj = {}
for (var a of arr) {
try { obj[a[0]].push(a[1]) }
catch(e) { obj[a[0]] = [a[1]] }
}
console.log(obj);
var new_arr = [];
for (var group in obj) {
new_arr.push([group, ...obj[group]])
}
console.log(new_arr);
Output:
[ [ 'group1', 'a', 'b' ], [ 'group2', 'c', 'd' ], [ 'group3', 'e' ] ]

I like #YuriKhristich solution. However, I still want to post this code showing how you could make a few minor changes to your existing code to build your required data structure.
We start by initializing a list with the group email. Next we append all the member emails to the list. And finally we push the completed list to memberArr. The advantage is that this builds the required data format while the data is read rather than trying to rework it afterward.
let list = [group]; // <-- ADD
if (members) {
members.forEach(member => {
// memberArr.push([group, member.email]) <-- REMOVE
list.push(member.email); // <-- ADD
})
memberArr.push(list); // <-- ADD
}
})
// Simulated Google Groups data
var gPage = {
groups: [
{id: 1, name: "Group 1", email: "group1#emal.com" },
{id: 2, name: "Group 2", email: "group2#emal.com" },
{id: 3, name: "Group 3", email: "group3#emal.com" }
]
};
var mPage = {
members: [
{id: 1, role: "admin", email: "member1#email.com" },
{id: 2, role: "admin", email: "member2#email.com" },
{id: 3, role: "admin", email: "member3#email.com" },
{id: 4, role: "admin", email: "member4#email.com" },
{id: 5, role: "admin", email: "member5#email.com" }
]
};
function getAllGroupsTEST() {
const groupArr = [];
let gPageToken;
//let gPage;
do {
/* useing fake data
gPage = AdminDirectory.Groups.list({
customer: "my_customer",
maxResults: 100,
gPageToken
});
*/
const groups = gPage.groups;
if (groups) {
groups.forEach(({email}) => {
const groupEmail = email;
groupArr.push(groupEmail);
});
}
gPageToken = gPage.nextPageToken;
} while (gPageToken);
const memberArr = [];
let mPageToken;
//let mPage;
groupArr.forEach(group => {
/* using fake data
mPage = AdminDirectory.Members.list(group,{
customer: "my_customer",
maxResults: 500,
mPageToken
})
*/
const members = mPage.members;
let list = [group];
if (members) {
members.forEach(member => {
// memberArr.push([group, member.email])
list.push(member.email);
})
memberArr.push(list);
}
})
return memberArr;
}
console.log( getAllGroupsTEST() );

You can implement this easy function for every group you want to filter
Take into account that this will eliminate every duplicate of the array.
const dataToFiler = [['Group 1', 'Foo'],
['Group 1', 'Bar'],
['Group 1', 'Baz'],
['Group 1', 'Quux']]
const filterRD = (arr) => {
return [...new Set(arr.flat())]
}
console.log(filterRD(dataToFiler))
Documentation
Set
Array.prototype.flat()

Related

Handle object array without duplicates

I use React for my front-end web app. When I call back-end API, I got this array:
[
{
id: 1,
fullname: ABC,
email: abc#gmail.com
...
},
{
id: 2,
fullname: DEF,
email: def#gmail.com
...
},
{
id: 2,
fullname: DEF,
email: def#gmail.com
...
},
{
id: 3,
fullname: GHI,
email: ghi#gmail.com
...
},
{
id: 1,
fullname: ABC,
email: abc#gmail.com
...
}
]
Now, I need to create a new array from this old array but just contain id and fullname and not duplicates. I have try this code:
const oldArray = //this is the array above
const temp = [];
for (let i = 0; i < oldArray .length; i++) {
temp.push({
value: oldArray [i]._id,
display: oldArray [i].fullname
});
}
const newArray = Array.from(new Set(temp));
The result I receive:
[
{
value: 1,
display: ABC
},
{
value: 2,
display: DEF
},
{
value: 2,
display: DEF
},
{
value: 3,
display: GHI
},
{
value: 1,
display: ABC
}
]
As you can see, the result is still duplicated. How can I fix it?
You can use Array#filter with a Set to store ids that were already found.
const arr=[{id:1,fullname:"ABC",email:"abc#gmail.com"},{id:2,fullname:"DEF",email:"def#gmail.com"},{id:2,fullname:"DEF",email:"def#gmail.com"},{id:3,fullname:"GHI",email:"ghi#gmail.com"},{id:1,fullname:"ABC",email:"abc#gmail.com"}];
let ids = new Set, res = arr.filter(x => !ids.has(x.id) && ids.add(x.id));
console.log(res);
const tempArray=[];
const filteredArr = oldArray.filter(value =>{
if(!tempArray.includes(value.id)) {
tempArray.push(value.id)
return true
}
})
Create a tempArray, loop over the array and checks if a value exist in tempArr if not push the value into the tempArr and return true(or value)
I know short answer but, use a Set() on the receiving end. Whenever you add an item to a set, use Set.add - it makes sure if it's a duplicate, it won't be added to the set. Sets have similar capability as arrays, but cannot contain duplicates.

How to loop inside a map() loop?

The below script outputs
================================================
Log entry ID: 1
UUID: 2
Timestamp: 3
--------------------
Log entry ID: 4
UUID: 5
Timestamp: 6
which is what I want.
Right now description is hard coded, where I would like it to be built using arr instead.
My current thinking were to somehow generate the inner array in the map() function:
[
`Log entry ID: ${element['_id']}`,
`UUID: ${element['_source'].uuid}`,
`Timestamp: ${element['_source']['#timestamp']}\n`,
]
but because of the templating, this is not even a valid array with 3 elements, when looking at it on its own. So I am all out of ideas.
Question
Somehow I have to loop over the elements in arr before it is given to map(), I suppose.
Does anyone know how that could be done?
const dedent = require('dedent');
const arr = [
[ 'Log entry ID', '_id' ],
[ 'UUID', 'uuid' ],
[ 'Timestamp', '#timestamp' ],
]
;
const docs = [
{'_id': 1,'_source': {'uuid': 2,'#timestamp': 3}},
{'_id': 4,'_source': {'uuid': 5,'#timestamp': 6}},
];
const description = dedent`
================================================
${
docs.map((element) => [
`Log entry ID: ${element['_id']}`,
`UUID: ${element['_source'].uuid}`,
`Timestamp: ${element['_source']['#timestamp']}\n`,
].join('\n')).join('--------------------\n')
}
`;
console.log(description);
Update
I control arr so changing it to eg. is possible, or something else
const arr = [
[ 'Log entry ID', '_id' ],
[ 'UUID', {'_source': 'uuid'} ],
[ 'Timestamp', {'_source': '#timestamp'} ],
]
;
Since arr is in your control perhaps you can specify the path of the key itself.
const arr = [
['Log entry ID', '_id'],
['UUID', '_source.uuid'],
['Timestamp', '_source.#timestamp'],
['Description', '_source._desc']
];
const docs = [{
'_id': 1,
'_source': {
'uuid': 2,
'#timestamp': 3,
'_desc': 'test 1'
}
},
{
'_id': 4,
'_source': {
'uuid': 5,
'#timestamp': 6,
'_desc': 'test 2'
}
},
];
const getValue = (object, keys) => keys.split('.').reduce((o, k) => (o || {})[k], object);
console.log(docs.map((element) => arr.map((label) => {
return `${label[0]}: ${getValue(element, label[1])}`
}).join('\n')).join('\n--------------------\n'))
Assuming that the keys of a docs array element are all unique, one could traverse the object looking for a matching key.
function findVal(object, key) {
var value;
Object.keys(object).some(function(k) {
if (k === key) {
value = object[k];
return true;
}
if (object[k] && typeof object[k] === 'object') {
value = findVal(object[k], key);
return value !== undefined;
}
});
return value;
}
docs.map((element) =>
arr.map(([item, key]) =>
`${item}: ${findVal(element, key)}`)
)
The FindVal is taken from here.
you could create a function to get data from arr and put in description
/* Simple Hello World in Node.js */
const arr = [
[ 'Log entry ID', '_id' ],
[ 'UUID', 'uuid' ],
[ 'Timestamp', '#timestamp' ],
]
;
const docs = [
{'_id': 1,'_source': {'uuid': 2,'#timestamp': 3}},
{'_id': 4,'_source': {'uuid': 5,'#timestamp': 6}},
];
const findInArr=function(value){
var t = ""
arr.forEach(item=>{
if (item.includes(value)) {t = item[0]}
})
return t
}
const description = dedent`
================================================
${
docs.map((element) => [
`${findInArr('_id')}: ${element['_id']}`,
`${findInArr('UUID')}: ${element['_source'].uuid}`,
`${findInArr('Timestamp')}: ${element['_source']['#timestamp']}\n`,
].join('\n')).join('--------------------\n')
}`;
console.log(description);

How to add label to js array?

Hi I am looking to create an array that looks similar to this
const userList = {
123: "Tom",
124: "Michael",
125: "Christin",
};
it contains both value and label, what I tried so far
let raw = []
for (let x in data) {
raw.push(data[x].facility_name : data[x].id)
}
but it didn't work because "," was expected, if someone can help please
You are confusing arrays and objects. You need to add a key to an object not push. I kept it as a for in loop, but a for of loop would make more sense.
const data = [
{ id: 1, facility_name: "foo1" },
{ id: 2, facility_name: "foo2" },
{ id: 3, facility_name: "foo3" }
];
let raw = {};
for (let x in data) {
raw[data[x].id] = data[x].facility_name;
}
console.log(raw);
How I would code it using reduce.
var data = [
{ id: 1, facility_name: "foo1" },
{ id: 2, facility_name: "foo2" },
{ id: 3, facility_name: "foo3" }
];
const raw = data.reduce(function (acc, facility) {
acc[facility.id] = facility.facility_name;
return acc;
}, {})
console.log(raw);
IF your data has nested objects then you might do this:
let raw = {};
for(x in data)
{
raw[data[x].facility_name] = data[x].id;
}
This is useful when you want to get rid of duplicates.

TS/JS - Get "value" from an Array of Objects if a Property of an Object from the Array matches another Property from a separate Object

THE PROBLEM:
I have an array of Objects. And a currentObject, that I currently am viewing. I want to get the value from a Property that comes from the Array of Objects, if 2 other properties match.
Here is the Array, simplified:
ARRAY = [{
id: 1,
metadata: {author: "Company1"}
},
{
id: 2,
metadata: {author: "Company2"}
}
Here is the Object, simplified:
OBJECT = {
name: "Something
templateId: 2
}
So, basically, I want to return, the metdata.author information, if the ARRAY.id, matches the OBJECT.templateId..
Here is the code I wrote..
const getAuthorInfo = (connTemplates: ARRAY[], currentConn: ITEM_OBJECT) => {
connTemplates.find((connTemplate: ARRAY_ITEM_OBJECT) => connTemplate.id === currentConn.templateId);
};
console.log('Author Info:', connection); // This though returns the OBJECT, not the ARRAY_ITEM
Any ideas, on how to make this work? I tried to filter as well, with the same condition, but that returned undefined, when I called it in my ReactComponent.
is this what you need?
const arr = [{
id: 1,
metadata: { author: "Company1" }
},
{
id: 2,
metadata: { author: "Company2" }
}]
const obj = {
name: "Something",
templateId: 2
}
function getAuthorInfo(arr, obj) {
const arrItem = arr.find(item => item.id === obj.templateId)
return arrItem.metadata.author
}
console.log(getAuthorInfo(arr, obj))
You are on the right path:
const result = arr.find(f => f.id == obj.templateId).metadata.author;
const arr = [{
id: 1,
metadata: {author: "Company1"}
},
{
id: 2,
metadata: {author: "Company2"}
}]
const obj = {
name: "Something",
templateId: 2
}
const result = arr.find(f => f.id == obj.templateId);
console.log(result);

ImmutableJS - pushing into a nested array

const List = Immutable.List;
const items = [
{ id: 1, subList: [] },
{ id: 2, subList: [] },
{ id: 3, subList: [] }
];
const newItem = { name: "sublist item" };
let collection = List(items);
collection = collection.updateIn([0, 'subList'], function (items) {
return items.concat(newItem)
});
https://jsbin.com/midimupire/edit?html,js,console
Results in:
Error: invalid keyPath
I think that perhaps I need to set subList as a List(); I get the same error when trying this.
If I understand the question correctly, you want to return collection with the first element as:
{
id : 1,
subList: [
{name: "sublist item"}
]
}
To do this we'll need to make a few changes.
Use Immutable.fromJS to deeply convert the plain JS array of objects to an Immutable List of Maps
Use List.update() to return a new List with the updated value
Use Map.updateIn() to return a new LMapist with the updated value
Here's the whole thing:
const List = Immutable.List;
const items = [{
id: 1,
subList: []
},
{
id: 2,
subList: []
},
{
id: 3,
subList: []
}
];
const newItem = {
name: "sublist item"
};
let collection = Immutable.fromJS(items);
collection = collection.update(0, item => {
return item.updateIn(['subList'], subList => {
return subList.concat(newItem);
});
});
console.log(collection)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>
And the result:
[
{
"id": 1,
"subList": [
{
"name": "sublist item"
}
]
},
{
"id": 2,
"subList": []
},
{
"id": 3,
"subList": []
}
]
Update: List.updateIn() can use an index as the keypath, so you can simplify this to the following:
collection = collection.updateIn([0, 'subList'], subList => {
return subList.concat(newItem);
});
Like this:
const List = Immutable.List;
const items = [{
id: 1,
subList: []
},
{
id: 2,
subList: []
},
{
id: 3,
subList: []
}
];
const newItem = {
name: "sublist item"
};
let collection = Immutable.fromJS(items);
collection = collection.updateIn([0, 'subList'], subList => {
return subList.concat(newItem);
});
console.log(collection)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>
Use the object you got, update the subArray and return the whole object.
const List = Immutable.List;
const items = [
{ id: 1, subList: [] },
{ id: 2, subList: [] },
{id: 3, subList: [] }
];
const newItem = { name: "sublist item" };
let collection = List(items);
collection = collection.update([0], function (obj) {
obj.subList = obj.subList.concat(newItem)
return obj;
});
This doesn’t work because the elements of your Immutable.List are plain-old JavaScript objects (POJO), not Immutable.Maps, so updateIn doesn’t know how to work with them. You can either:
Make the objects Immutable.Maps by using Immutable.fromJS instead of Immutable.List as the constructor to convert the entire object graph to Immutable objects. (See JS Bin)
Use update([0]) instead of updateIn to just get the POJO and mutate it (as in #Navjot’s answer).

Categories

Resources