How to get array of arrays by grouping ids - javascript

I have an array:
let ar = [{
asst: 1,
v: 'b'
}, {
emp: 4
}, {
journal_id: 3
}, {
asst: 4
}, {
asst: 1,
v: 'a'
}, {
asst: 1,
v: 'c'
}, {
journal_id: 3
}, {
journal_id: 3
}]
I want an array that has asst and journal_id as array of arrays and emp as just an object in the array that has array of asst and journal_id.
Like this:
[[{asst:1, v: 'a'}, {asst:1, v: 'b'},{asst:1, v: 'c'}], [{asst:4}], [{journal_id:3}, {journal_id:3}, {journal_id:3}], {emp:4}]
I have tried this:
let ar = [{
asst: 1,
v: 'b'
}, {
emp: 4
}, {
journal_id: 3
}, {
asst: 4
}, {
asst: 1,
v: 'a'
}, {
asst: 1,
v: 'c'
}, {
journal_id: 3
}, {
journal_id: 3
}]
let asstArray = [],
journalArray = [],
fullArray = [];
for (let i = 0; i < ar.length; i++) {
debugger
if (ar[i].asst) {
let contains = false;
for (let j = 0; j < asstArray.length; j++) {
for (let k = 0; k < asstArray[j].length; k++) {
if (asstArray[j][k].asst == ar[i].asst) {
contains = true;
let d = asstArray[j][k];
}
}
if (contains) {
asstArray[j].push(ar[i]);
}
}
if (!contains) {
asstArray.push([ar[i]]);
}
} else if (ar[i].journal_id) {
let contains = false;
for (let j = 0; j < journalArray.length; j++) {
for (let k = 0; k < journalArray[j].length; k++) {
if (journalArray[j][k].journal_id == ar[i].journal_id) {
contains = true;
}
}
if (contains) {
journalArray[j].push(ar[i]);
}
}
if (!contains) {
journalArray.push([ar[i]]);
}
}
}
fullArray.push(asstArray);
fullArray.push(journalArray);
console.log(fullArray, "Full")
JS Fiddle

Here is a succint way to do it with reduce and Object.values. Also, the keys to check are declared in the initial value to the reduce function.
By using ES6 desctructuring, you can then rebuild the array you want at the end, with emp, out of the inner arrays.
With emp in the array:
const ar = [
{ asst: 1, v: 'b' },
{ emp: 4 },
{ journal_id: 3 },
{ asst: 4 },
{ asst: 1, v: 'a' },
{ asst: 1, v: 'c' },
{ journal_id: 3 },
{ journal_id: 3 }
];
const result = Object.values(ar.reduce((accum, e) => {
Object.keys(accum).forEach(k => {
if (k in e) accum[k].push(e);
});
return accum;
}, { asst: [], journal_id: [], emp: [] }));
console.log(result);
With emp outside the inner array:
const ar = [
{ asst: 1, v: 'b' },
{ emp: 4 },
{ journal_id: 3 },
{ asst: 4 },
{ asst: 1, v: 'a' },
{ asst: 1, v: 'c' },
{ journal_id: 3 },
{ journal_id: 3 }
];
let result = ar.reduce((accum, e) => {
Object.keys(accum).forEach(k => {
if (k in e) accum[k].push(e);
});
return accum;
}, { asst: [], journal_id: [], emp: [] });
const { emp, ...otherProps } = result;
result = [...Object.values(otherProps), emp[0]];
console.log(result);

You could combine the values of the wanted grouping keys to a single key and group by this value.
Objects without grouping keys are pushed to the result set directly.
var array = [{ asst: 1, v: 'b' }, { emp: 4 }, { journal_id: 3 }, { asst: 4 }, { asst: 1, v: 'a' }, { asst: 1, v: 'c' }, { journal_id: 3 }, { journal_id: 3 }], keys = ['asst', 'journal_id'],
keys = ['asst', 'journal_id'],
result = array.reduce((hash => (r, o) => {
if (keys.some(k => k in o)) {
var key = keys.map(k => o[k]).join('|');
if (!hash[key]) {
r.push(hash[key] = []);
}
hash[key].push(o);
} else {
r.push(o);
}
return r;
})({}), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can reduce the array in a Map and then create a new array with the Map values:
const data = [{"asst":1,"v":"b"},{"emp":4},{"journal_id":3},{"asst":4},{"asst":1,"v":"a"},{"asst":1,"v":"c"},{"journal_id":3},{"journal_id":3}];
const grouped = data.reduce(
(result,item)=>{
//key is item.asst or item.jornal_id or other
const key = item.asst || item.journal_id || 'other';
//set result(key) with empty array or existing array and conat new item
return result.set(key,(result.get(key)||[]).concat(item));
},new Map()
);
//get other values or empty array if they don't exist:
const others = grouped.get('other') || [];
//remove others if they exist:
grouped.delete('other');
//log array from Map values
console.log(others.concat([...grouped.values()]));

Related

Return similar values ​from multiple array of objects in Javascript

Guys I made a simple example to illustrate my problem. I have 3 object arrays, datasOne, datasTwo and datasThree and what I want is to return a new array only with the objects that are in the 3 arrays. For example, if there is only Gustavo in the 3 arrays, then he will be returned. But there is a detail that if the datasThree is an empty array, then it will bring the data in common only from datasOne and datasTwo and if only the datasTwo which has data and the other two arrays have empty, then it will return data only from datasTwo. In other words it is to return similar data only from arrays that have data. I managed to do this algorithm and it works the way I want, but I would like to know another way to make it less verbose and maybe simpler and also work in case I add more arrays to compare like a dataFour for example. I appreciate anyone who can help me.
My code below:
let datasOne = [
{ id: 1, name: 'Gustavo' },
{ id: 2, name: 'Ana' },
{ id: 3, name: 'Luiz' },
{ id: 8, name: 'Alice' }
]
let datasTwo = [
{ id: 1, name: 'Gustavo' },
{ id: 3, name: 'Luiz' },
{ id: 8, name: 'Alice' }
]
let datasThree = [
{ id: 1, name: 'Gustavo' },
{ id: 3, name: 'Luiz' },
{ id: 2, name: 'Ana' },
{ id: 5, name: 'Kelly' },
{ id: 4, name: 'David' }
]
let filtered
if (datasOne.length > 0 && datasTwo.length > 0 && datasThree.length > 0) {
filtered = datasOne.filter(firstData => {
let f1 = datasThree.filter(
secondData => firstData.id === secondData.id
).length
let f2 = datasTwo.filter(
secondData => firstData.id === secondData.id
).length
if (f1 && f2) {
return true
}
})
} else if (datasOne.length > 0 && datasTwo.length > 0) {
filtered = datasOne.filter(firstData => {
return datasTwo.filter(secondData => firstData.id === secondData.id).length
})
} else if (datasOne.length > 0 && datasThree.length > 0) {
filtered = datasOne.filter(firstData => {
return datasThree.filter(secondData => firstData.id === secondData.id)
.length
})
} else if (datasTwo.length > 0 && datasThree.length > 0) {
filtered = datasTwo.filter(firstData => {
return datasThree.filter(secondData => firstData.id === secondData.id)
.length
})
} else if (datasThree.length > 0) {
filtered = datasThree
} else if (datasTwo.length > 0) {
filtered = datasTwo
} else if (datasOne.length) {
filtered = datasOne
}
console.log(filtered)
1) You can first filter the array which is not empty in arrs.
const arrs = [datasOne, datasTwo, datasThree].filter((a) => a.length);
2) Flatten the arrs array using flat().
arrs.flat()
3) Loop over the flatten array and count the occurrence of all objects using Map
const map = new Map();
for (let o of arrs.flat()) {
map.has(o.id)
? (map.get(o.id).count += 1)
: map.set(o.id, { ...o, count: 1 });
}
4) Loop over the map and collect the result only if it is equal to arrs.length
if (count === arrs.length) result.push(rest);
let datasOne = [
{ id: 1, name: "Gustavo" },
{ id: 2, name: "Ana" },
{ id: 3, name: "Luiz" },
{ id: 8, name: "Alice" },
];
let datasTwo = [
{ id: 1, name: "Gustavo" },
{ id: 3, name: "Luiz" },
{ id: 8, name: "Alice" },
];
let datasThree = [
{ id: 1, name: "Gustavo" },
{ id: 3, name: "Luiz" },
{ id: 2, name: "Ana" },
{ id: 5, name: "Kelly" },
{ id: 4, name: "David" },
];
const arrs = [datasOne, datasTwo, datasThree].filter((a) => a.length);
const map = new Map();
for (let o of arrs.flat()) {
map.has(o.id)
? (map.get(o.id).count += 1)
: map.set(o.id, { ...o, count: 1 });
}
const result = [];
for (let [, obj] of map) {
const { count, ...rest } = obj;
if (count === arrs.length) result.push(rest);
}
console.log(result);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
Not 100% sure it cover all edge cases, but this might get you on the right track:
function filterArrays(...args) {
const arraysWithData = args.filter((array) => array.length > 0);
const [firstArray, ...otherArrays] = arraysWithData;
return firstArray.filter((item) => {
for (const array of otherArrays) {
if (!array.some((itemTwo) => itemTwo.id === item.id)) {
return false;
}
}
return true;
});
}
Usage:
const filtered = filterArrays(datasOne, datasTwo, datasThree);
console.log(filtered)
I believe the code is fairly readable, but if something is not clear I'm glad to clarify.
function merge(arr){
arr = arr.filter(item=>item.length>0)
const map = {};
arr.forEach(item=>{
item.forEach(obj=>{
if(!map[obj.id]){
map[obj.id]=[0,obj];
}
map[obj.id][0]++;
})
})
const len = arr.length;
const ret = [];
Object.keys(map).forEach(item=>{
if(map[item][0]===len){
ret.push(map[item][1])
}
})
return ret;
}
merge([datasOne,datasTwo,datasThree])

Get All The Child Record from a tree type object array Javascript

I have one JSON data like below
var data =
[
{
"id":1,
"parentId":0
},
{
"id":2,
"parentId":1
},
{
"id":3,
"parentId":1
},
{
"id":4,
"parentId":2
},
{
"id":5,
"parentId":4
},
{
"id":6,
"parentId":3
},
{
"id":7,
"parentId":6
},
{
"id":8,
"parentId":7
}
]
now I want to get all the child of id=2 or 3
for eg:
the childrens of id 2 are:(4,5)
for 3:
the childrens of id 2 are:(6,7,8)
how can I get all the child record
I tried using for loop but failed below is what my work
here ArrFolderTree = data;
let parentId = [];
let arrAdminFolder = [];
for (let i = ArrFolderTree.length - 1; i > 0; i--) {
let ind = 0;
while (ind < ArrFolderTree.length) {
for (let o = 0; o < ArrAllFolders.length; o++) {
if (ArrAllFolders[o].FolderId == ArrFolderTree[ind].Id) {
parentId.push(ArrFolderTree[ind].Id);
arrAdminFolder.push(ArrFolderTree[ind].Id);
}
let currentParentId = 0;
for (let ab = 0; ab < parentId.length; ab++) {
if (parentId[ab] == ArrAllFolders[o].FolderId) {
currentParentId = ArrFolderTree[ind].Id;
arrAdminFolder.push(ArrFolderTree[ind].Id);
}
}
if (currentParentId > 0)
parentId.push(currentParentId);
}
ind++;
}
}
the above loop is wrong but i want to know how can i use the loop here
You could take an object where all nodes are sorted by their parentId and then take the id for finding the children and the nested children by a recursion.
const
getChildren = id => (relations[id] || []).flatMap(o => [o, ...getChildren(o.id)]),
data = [{ id: 1, parentId: 0 }, { id: 2, parentId: 1 }, { id: 3, parentId: 1 }, { id: 4, parentId: 2 }, { id: 5, parentId: 4 }, { id: 6, parentId: 3 }, { id: 7, parentId: 6 }, { id: 8, parentId: 7 }],
relations = data.reduce((r, o) => {
(r[o.parentId] ??= []).push(o);
return r;
}, {});
console.log(getChildren(2)); // 4 5
console.log(getChildren(3)); // 6 7 8
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to remove object in array of objects with matching values

I want to check my array for objects with matching values, if they match remove the object with the lowest index as that will be the one is "older"
I had success using this method for removing duplicate objects in the array, but when i get to specific values of those objects i'm not sure
someFunction() {
let cart = this.state.currentUser.cart
const newCartArray = cart.filter((light, index) => {
return index === cart.findIndex(obj => {
obj.use === light.use
})
})
cart = newCartArray
}
You could take a Map and store the last object with a wanted key and get as result only the last stored objects.
var array = [{ id: 1, index: 0 }, { id: 2, index: 1 }, { id: 3, index: 2 }, { id: 2, index: 3 }, { id: 3, index: 4 }, { id: 1, index: 5 }, { id: 4, index: 6 }, { id: 5, index: 7 }],
result = Array.from(array.reduce((m, o) => m.set(o.id, o), new Map).values());
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you like to keep the original order, you could check the same object reference for filtering.
var array = [{ id: 1, index: 0 }, { id: 2, index: 1 }, { id: 3, index: 2 }, { id: 2, index: 3 }, { id: 3, index: 4 }, { id: 1, index: 5 }, { id: 4, index: 6 }, { id: 5, index: 7 }],
map = array.reduce((m, o) => m.set(o.id, o), new Map),
result = array.filter(o => o === map.get(o.id));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
let cart = this.state.currentUser.cart;
let index = cart.indexOf(light);
if( index != -1) {
cart.splice( index,1);
}
or if you need to check the .use
let cart = this.state.currentUser.cart;
for( let i =0; i < cart.length; i++) {
if( cart[i].use === light.use) {
cart.splice(i,1);
break;
}
}
You could filter out all the items that have subsequent items match the relevant property, like so:
const newCartArray = cart.filter((light, i, array) => {
return !array.slice(i + 1).some(obj => obj.use === light.use);
})
This should work:
someFunction() {
let cart = this.state.currentUser.cart
const newCartArray = cart.filter((light, index) => {
return cart.slice(index + 1).findIndex(obj => {
obj.use === light.use
}) === -1;
})
cart = newCartArray
}

How can I rearrange an object key to the beginning of a grouped object

My question is in the code comments:
var myObj = [
{
0: 'company1',
1: { count: 3 }
},
{
0: 'company2',
1: { count: 3 }
},
{
0: 'company3',
1: { count: 2 }
},
{
0: 'company1',
1: { count: 1 }
},
];
var companytoshift = 'company2';
var f = [];
for (var i = 0; i < myObj.length; i++) {
f.push(myObj[i][1].count);
}
var isMultipleSameValues = samevaluemultiplecompany(f);
if (isMultipleSameValues) { //we have multiple company with same value
/*
if 'companytoshift' exist in the object array
if 'companytoshift' has a count value that is same with other companies
then shift that company to the top of the same count value group.
For the example object above, after we perform the function, it would be this:
var obj = [
{
0: 'company2',
1: {
count: 3
}
},
{
0: 'company1',
1: {
count: 3
}
},
{
0: 'company3',
1: {
count: 2
}
},
{
0: 'company1',
1: {
count: 1
}
},
];
*/
/* as you can see company2 moved above company1 because it had the same count value as another company, which is 3, and also because it was the company in the `companytoshift` variable
*/
}
function samevaluemultiplecompany(a) {
var counts = [];
for (var i = 0; i <= a.length; i++) {
if (counts[a[i]] === undefined) {
counts[a[i]] = 1;
} else {
return true;
}
}
return false;
}
(fiddle)
You could find the count of the given company and sort by company first and then by the count. By having a stable sort, the other elements are unsorted at the end of the array.
function sort(array, company) {
var count = array.find(([c]) => c === company)[1].count;
return array.some((c => ({ 1: { count: v } }) => v === count && ++c === 2)(0))
? array.sort((a, b) =>
(b[0] === company) - (a[0] === company) ||
(b[1].count === count) - (a[1].count === count)
)
: array;
}
var array = [
["company1", { count: 3 }],
["company3", { count: 1 }],
["company2", { count: 3 }],
["company4", { count: 0 }],
["company5", { count: 0 }]
];
console.log(sort(array, "company2"));
console.log(sort(array, "company3"));
console.log(sort(array, "company5"));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Merge arrays in JS

Suppose I have the following arrays:
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, field: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
]
var third = [
{ id: 2, data: 'some2' },
{ id: 5, data: 'some5' },
{ id: 6, data: 'some6' }
]
I want to merge them to get the following result:
var result = [
{ id: 1, name: 'first', field: undefined, data: undefined },
{ id: 2, name: 'second', field: 'foo2', data: 'some2' },
{ id: 3, name: 'third', field: 'foo3', data: undefined },
{ id: 4, name: undefined, field: 'foo4', data: undefined },
{ id: 5, name: undefined, field: undefined, data: 'some5' },
{ id: 6, name: undefined, field: undefined, data: 'some6' }
]
How could I do it with JavaScript?
You should get all existed keys and after create new Objects with fill "empty" keys:
function mergeArrays(){
var keys = {};
//save all existed keys
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
for(var key in arguments[i][j]){
keys[key] = true;
}
}
}
var res = [];
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
//set clone of object
var clone = JSON.parse(JSON.stringify(arguments[i][j]));
for(var key in keys){
if(!(key in clone)){
clone[key] = undefined;
}
}
res.push(clone);
}
}
return res;
}
https://jsfiddle.net/x3b0tk3g/
There is no simple solution for what you want. Here is my suggestion.
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, filed: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
];
var third = [
{ id: 2, data: 'some2' },
{ id: 4, data: 'some4' },
{ id: 6, data: 'some6' }
];
var result = {};
first.concat(second,third).forEach(function(item){
var id = item.id;
var row = result[id];
if(!row){
result[id] = item;
return;
}
for(var column in item){
row[column] = item[column];
}
});
var finalResult = Object.keys(result).map(function(id){
return result[id];
});
console.log(finalResult);
fiddle: http://jsfiddle.net/bs20jvnj/2/
function getByProperty(arr, propName, propValue) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][propName] == propValue) return arr[i];
}
}
var limit = first.length + second.length + third.length;
var res = [];
for (var i = 1; i < limit; i++) {
var x = $.extend({}, getByProperty(first, "id", i), getByProperty(second, "id", i), getByProperty(third, "id", i));
console.log(x["id"]);
if (x["id"] === undefined) x["id"] = i;
res.push(x);
}
console.log(res);
There's probably a shorter way to solve this, but this covers all the steps, including ensuring that there are default properties that are undefined if not found. It also takes any number of input arrays, and you can specify what default keys you require if they're not already covered by the keys in the existing objects, so pretty future-proof for your needs.
// merges the key/values of two objects
function merge(a, b) {
var key;
if (a && b) {
for (key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
}
return a;
}
function concatenate() {
var result = [];
var args = arguments[0];
for (var i = 0, l = args.length; i < l; i++) {
result = result.concat(args[i]);
}
return result;
}
// return a default object
function getDefault() {
return {
id: undefined,
name: undefined,
data: undefined,
field: undefined
};
}
// loop over the array and check the id. Add the id as a key to
// a temporary pre-filled default object if the key
// doesn't exist, otherwise merge the existing object and the
// new object
function createMergedArray(result) {
var temp = {};
var out = [];
for (var i = 0, l = result.length; i < l; i++) {
var id = result[i].id;
if (!temp[id]) temp[id] = getDefault();
merge(temp[id], result[i]);
}
// loop over the temporary object pushing the values
// into an output array, and return the array
for (var p in temp) {
out.push(temp[p]);
}
return out;
}
function mergeAll() {
// first concatenate the objects into a single array
// and then return the results of merging that array
return createMergedArray(concatenate(arguments));
}
mergeAll(first, second, third);
DEMO

Categories