Loop through two arrays and get a desired output using javascript - javascript

I have two arrays as follows
const arr1 = ['john', 'robot'];
const arr2 = [{name : 'john'}, {name : 'kevin'}, {name : 'james}];
My desired output is to manipulate array arr1 such that if value of arr1 is not present as a name property in arr2 it should get removed from arr1;
So in the above example after the operation, the output should be
console.log(arr1) // ['john']
as robot is not present in arr2, it gets removed.
Likewise if I have following set of arrays
const names = ['sachin', 'sehwag'];
const players = [{name : 'dhoni'}, {name : 'dravid'}, {name : 'ganguly'} , {name : 'laxman}];
names array should be manipulated to
console.log(names) // []
as both sachin and sehwag is not present in players array
Please help.Thanks in advance.

If you can use es6, a Set is good for this. They only store unique values and are efficient at lookup. They give constant time lookups, which looping through an array doesn't:
const arr1 = ['john', 'robot'];
const arr2 = [{name : 'john'}, {name : 'kevin'}, {name : 'james'}];
// create a set with names
let testSet = arr2.reduce((set, obj) => set.add(obj.name), new Set)
let filtered = arr1.filter(item => testSet.has(item))
console.log(filtered)

You can first grab all the names from array of objects arr2, and then filter the arr1 based on names list..
var arr1 = ["john", "robot"];
var arr2 = [{ name: "john" }, { name: "kevin" }, { name: "james" }];
var names = arr2.map(ob => ob.name);
console.log(arr1.filter(name => names.includes(name)));
var arr1 = ["sachin", "sehwag"];
var arr2 = [
{ name: "dhoni" },
{ name: "dravid" },
{ name: "ganguly" },
{ name: "laxman" }
];
var names = arr2.map(ob => ob.name);
console.log(arr1.filter(name => names.includes(name)));

const arr1 = ['john', 'robot'];
const arr2 = [{name : 'john'}, {name : 'kevin'}, {name : 'james'}];
var newArray= [];
for(var i in arr2) {
var content = arr2[i]['name'];
if(arr1.indexOf(content) > -1){
newArray.push(content);
}
}
console.log(newArray);
Hope it helps you!

This sounds like a job for [].filter:
function doTheThingYouWantItToDo(a, b) {
return a.filter(function(elem) {
for (var i = 0; i < b.length; i+=1) {
if (b[i].name == elem) {
return true;
}
}
return false;
});
}

Related

Merge two arrays by name value in javascript [duplicate]

This question already exists:
Merge 2 arrays by name javascript [duplicate]
Closed 9 months ago.
var arr1 = [{name: "lang", age: "23"},{name:"shen", "age" : "18"}];
var arr2 = [{name : "shen Fajarda", status: "married"}];
How to merge this 2 arrays by name though the name in arr1 has the name shen while the arr2 has the whole name shen Fajarda.
This the output that i need,
var arr3 = [{name: "lang", age: "23"},{name:"shen", age : "18",status:"married"}];
Just map over each item in the first item and merge with the corresponding item in the second array if they can be found.
const arr1 = [{name: "lang", age: "23"},{name: "shen", "age": "18"}];
const arr2 = [{name: "shen Fajarda", status: "married"}];
const merged = arr1.map(person => {
const name = person.name.toLowerCase();
const otherRecord = arr2.find(({name: candidateName}) => candidateName.toLowerCase().includes(name));
return otherRecord ? {...person, ...otherRecord} : person;
});
You can use reduce and inside the reduce callback check if the second array contains same name using find
let arr1 = [{
name: "lang",
age: "23"
}, {
name: "shen",
"age": "18"
}];
let arr2 = [{
name: "shen Fajarda",
status: "married"
}];
const mergedVal = arr1.reduce((acc, curr) => {
const name = curr.name.toLowerCase();
const isNameAvl = arr2.find(elem => elem.name.toLowerCase().includes(name));
isNameAvl && acc.push({ ...curr,
...isNameAvl
});
!isNameAvl && acc.push(curr);
return acc;
}, []);
console.log(mergedVal)

FIlter Array and get count of filtered array

I want to filter following two arrays and get the count of "isimplemented: 'Yes'" elements:
const arr1 = [{ProjectName: "IT", Department: "Software"}]
const arr2 = [{Name: "IT", isimplemented: "Yes"}]
I tried the following method to do the same but not getting desired result. How I can do it in JavaScript
((arr1.map(data => data.ProjectName)).filter(arr1.map(data => data.ProjectName) === arr2.map(data => data.Name) && isimplemented === "Yes")).length
You could teka a Set for the implemented projects and then count the occurences of the project who are implemented.
const
arr1 = [{ ProjectName: "IT", Department: "Software" }],
arr2 = [{ Name: "IT", isimplemented: "Yes" }],
implemented = arr2.reduce((s, { Name, isimplemented }) => isimplemented === 'Yes' ? s.add(Name) : s, new Set),
count = arr1.reduce((c, { ProjectName }) => c + implemented.has(ProjectName), 0);
console.log(count);
First merge the different arrays to create one single array
Create a variable which will keep track of your count
use forEach to iterate over the elements in the array, increment the count for every array-object having property isImplemented: 'Yes'
const arr1 = [{ProjectName: "IT", Department: "Software"}]
const arr2 = [{Name: "IT", isimplemented: "Yes"}]
const newArr = [...arr1, ...arr2]
let count = 0
newArr.forEach(element => {
if (element.isimplemented === 'Yes') {
count++
}
})
console.log(count)

How to count number of occurrences of distinct values from an array of objects in Javascript?

I have an array of objects like below:
var array =
[
{"name":"abc","age":20}
{"name":"abc","age":20}
{"name":"abc","age":20}
{"name":"xyz","age":21}
{"name":"xyz","age":21}
]
I want to count the number of occurrences of distinct values like:
[3,2]
Assuming abc has 3 occurrences and xyz has 2 occurrences.
I am doing it in reactjs. I am able to get distinct values like [abc,xyz] using this answer.
ES6 syntax is preferred.
You'll need to know to which name a count belongs, so I propose not to output an array that gives you no clue about that, but an object keyed by names and with as value the corresponding count:
var result = array.reduce( (acc, o) => (acc[o.name] = (acc[o.name] || 0)+1, acc), {} );
var array =
[
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"xyz","age":21},
{"name":"xyz","age":21}
];
var result = array.reduce( (acc, o) => (acc[o.name] = (acc[o.name] || 0)+1, acc), {} );
console.log(result);
Map/Reduce to the rescue:
const frequency = array
.map(({ name }) => name)
.reduce((names, name) => {
const count = names[name] || 0;
names[name] = count + 1;
return names;
}, {});
// frequency: { abc: 3, xyz: 2 }
You can use forEach/map to iterate the array and store the count in another variable, Check this:
var array = [
{"name" : "abc", "age" : 20},
{"name" : "abc", "age" : 20},
{"name" : "abc", "age" : 20},
{"name" : "xyz", "age" : 21},
{"name" : "xyz", "age" : 21},
];
let b = {};
array.forEach(el => {
b[el.name] = (b[el.name] || 0) + 1;
})
console.log(b);
Still if anyone looking for distinct counts stored in an array
var result = array.reduce( (acc, o) => (acc[o.name] = (acc[o.name] || 0)+1, acc), {} );
result = Object.values(result); // returns an array of values from the object
// result will be [3,2]
This is one way to do it:
var array =
[
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"xyz","age":21},
{"name":"xyz","age":21}
]
let yy = {}
array.map( el => {
yy[el.name] = (yy[el.name] || 0) + 1
})
console.log(yy)
And this is another way:
var array =
[
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"xyz","age":21},
{"name":"xyz","age":21}
]
let yy = {}
array.map( el => {
if (yy[el.name] === undefined || yy[el.name] === 0) {
yy[el.name] = 1
} else {
yy[el.name] = yy[el.name] + 1
}
})
console.log(yy)
Quick answer : new Set(array).size.
Explanation (From MDN Web Docs):
The Set object lets you store unique values of any type, whether
primitive values or object references

Aggregate and transform array of objects

Using javascript functional programming methods (like map/reduce), how would one aggregate/count the status field of arr1 and transform it to an array of key/value objects in arr2.
arr1 = [
{'task':'do something 1', 'status':'done'} ,
{'task':'do something 2', 'status':'done'} ,
{'task':'do something 3', 'status':'pending'} ,
{'task':'do something 4', 'status':'done'}
];
// Aggregate arr1 `status` field and transform to:
arr2 = [
{key:'done', value: 3},
{key:'pending', value: 1}
];
Here's my WIP partial solution that handles only the aggregation portion. I still need the transform portion.
var arr2 = arr1.map(function(item) {
return item.status;
}).reduce(function(acc,curr,idx){
if(acc[curr] === undefined) acc[curr] = 1;
else acc[curr] += 1;
return acc;
}, []);
Here's the most functional way I could come up with. This includes both the aggregation and your desired transform:
var arr2 = Object.keys(arr2 = arr1.map(function(item) {
return item.status;
}).reduce(function(acc,curr){
acc[curr] = acc[curr] + 1 || 1;
return acc;
}, [])).map(function(item){
return {key: item, value: arr2[item]}
});
You can try Array.prototype.forEach(). Also instead of using an array, you can use object. This will save you looping over to find count of a specific status.
arr1 = [
{'task':'do something 1', 'status':'done'} ,
{'task':'do something 2', 'status':'done'} ,
{'task':'do something 3', 'status':'pending'} ,
{'task':'do something 4', 'status':'done'}
];
var result = {};
arr1.forEach(function(item){
if(!result[item.status])
result[item.status] = 0;
result[item.status]++;
});
console.log(result);
Since you are storing the output value as an array you should check weather the key status is present or not. If present increment its value.
arr2 = [];
arr1.forEach(function(item){
var keyPresent = false;
for(var i = 0, len = arr2.length; i < len; i++) {
if( arr2[ i ].key === item.status ){
keyPresent = true;
arr2[ i ].value++
}
}
if(!keyPresent){
arr2.push({key: item.status , value : 1})
}
The output given by
arr2 = [
{key:'done', value: 3},
{key:'pending', value: 1}
];
const arr_done = arr1.filter(item => item.status == 'done');
const arr_pending = arr1.filter(item => item.status === 'pending');
arr2 = [
{key:'done', value: arr_done.length},
{key:'pending', value: arr_pending.length}
];
console.log(arr2);

JavaScript merging objects by id [duplicate]

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
How to merge two arrays in JavaScript and de-duplicate items
(89 answers)
Closed 7 months ago.
What's the correct way to merge two arrays in Javascript?
I've got two arrays (for example):
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
I want to be able to end up with something like:
var a3 = [{ id : 1, name : "test", count : "1"},
{ id : 2, name : "test2", count : "2"}]
Where the two arrays are being joined based on the 'id' field and extra data is simply being added.
I tried to use _.union to do this, but it simply overwrites the values from the second array into the first one
Short ES6 solution
const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))
This should do the trick:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
});
This assumes that the id of the second object in a1 should be 2 rather than "2"
Assuming IDs are strings and the order does not matter, you can
Create a hash table.
Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, update it with Object.assign (ES6, can be polyfilled).
Get an array with the values of the hash map.
var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
return hash[key];
});
In ECMAScript6, if the IDs are not necessarily strings, you can use Map:
var hash = new Map();
a1.concat(a2).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());
ES6 simplifies this:
let merge = (obj1, obj2) => ({...obj1, ...obj2});
Note that repeated keys will be merged, and the value of the second object will prevail and the repeated value of the first object will be ignored.
Example:
let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};
merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}
Complete solution (with Lodash, not Underscore)
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1"}
1: {id: 2, name: "test2", count: "2"}
If you have an array of arrays to merge you can do it like this:
var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}
reduce version.
var a3 = a1.concat(a2).reduce((acc, x) => {
acc[x.id] = Object.assign(acc[x.id] || {}, x);
return acc;
}, {});
_.values(a3);
I think it's common practice in functional language.
Already there are many great answers, I'll just add another one which is from a real problem I needed to solve yesterday.
I had an array of messages with user ids, and one array of users containing users' names and other details. This is how I managed to add user details to the messages.
var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];
var messagesWithUserNames = messages.map((msg)=> {
var haveEqualId = (user) => user.id === msg.userId
var userWithEqualId= users.find(haveEqualId)
return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)
Vanilla JS solution
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
const merge = (arr1, arr2) => {
const temp = []
arr1.forEach(x => {
arr2.forEach(y => {
if (x.id === y.id) {
temp.push({ ...x, ...y })
}
})
})
return temp
}
console.log(merge(a1, a2))
The lodash implementaiton:
var merged = _.map(a1, function(item) {
return _.assign(item, _.find(a2, ['id', item.id]));
});
The result:
[
{
"id":1,
"name":"test",
"count":"1"
},
{
"id":2,
"name":"test2",
"count":"2"
}
]
Wanted to add this answer which is derived from #daisihi answer above. Main difference is that this uses the spread operator.
Also, at the end I remove the id because it was not desirable in the first place.
const a3 = [...a1, ...a2].reduce((acc, x) => {
acc[x.id] = {...acc[x.id] || {}, ...x};
return acc;
}, {});
This part was taken from another post. removing a property from a list of objects in an array
const newArray = Object.values(a3).map(({id, ...keepAttrs}) => keepAttrs);
Found other solutions failing for some cases, so writing a better one here
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 3, count : "3"}, { id : 1, count : "1"}, {id : 2, count : "2"}]
const mergeHelper = new Map(a1.map(x => [x.id, x]));
for (const x of a2) {
if (mergeHelper.has(x.id)) {
const item = mergeHelper.get(x.id);
mergeHelper.set(x.id, {...item, ...x});
} else {
mergeHelper.set(x.id, x);
}
}
const mergedList = [...mergeHelper.values()];
// For sorted array
// const mergedSortedList = [...mergeHelper.values()].sort((a, b) => a.id - b.id);
console.log(mergedList)
Using js Map is way faster than other approaches, helps when array length is huge.
A working TypeScript version:
export default class Merge {
static byKey(a1: any[], a2: any[], key: string) {
const res = a1.concat(a2).reduce((acc, x) => {
acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
return acc;
}, {});
return Object.entries(res).map(pair => {
const [, value] = pair;
return value;
});
}
}
test("Merge", async () => {
const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
const a2 = [{ id: "2", value: "3" }];
expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
{
id: "1",
value: "1"
},
{ id: "2", value: "3" }
]);
});
try this
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
let arr3 = a1.map((item, i) => Object.assign({}, item, a2[i]));
console.log(arr3);
How about this?
const mergeArrayObjects = (arr1: any[], arr2: any[], mergeByKey: string): any[] => {
const updatedArr = [];
for (const obj of arr1) {
const arr1ValueInArr2 = arr2.find(
a => a[mergeByKey] === obj[mergeByKey],
);
if (arr1ValueInArr2) {
updatedArr.push(Object.assign(obj, arr1ValueInArr2));
} else {
updatedArr.push(obj);
}
}
const mergeByKeyValuesInArr1 = arr1.map(a => a[mergeByKey]);
const remainingObjInArr2 = arr2.filter(a => !mergeByKeyValuesInArr1.includes(a[mergeByKey]) )
return updatedArr.concat(remainingObjInArr2)
}
You can write a simple object merging function like this
function mergeObject(cake, icing) {
var icedCake = {}, ingredient;
for (ingredient in cake)
icedCake[ingredient] = cake[ingredient];
for (ingredient in icing)
icedCake[ingredient] = icing[ingredient];
return icedCake;
}
Next, you need to do use a double-loop to apply it to your data structre
var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i) // for each item in a2
for (j = 0; i < a3.length; ++i) // look at items in other array
if (a2[i]['id'] === a3[j]['id']) // if matching id
a3[j] = mergeObject(a3[j], a2[i]); // merge
You can also use mergeObject as a simple clone, too, by passing one parameter as an empty object.
const a3 = a1.map(it1 => {
it1.test = a2.find(it2 => it2.id === it1.id).test
return it1
})
If you have exactly the same number of items in both array with same ids you could do something like this.
const mergedArr = arr1.map((item, i) => {
if (item.ID === arr2[i].ID) {
return Object.assign({}, item, arr2[i]);
}
});
function mergeDiffs(Schedulearray1, Schedulearray2) {
var secondArrayIDs = Schedulearray2.map(x=> x.scheduleid);
return Schedulearray1.filter(x=> !secondArrayIDs.includes(x.scheduleid)).concat(Schedulearray2);
}
None of them worked for me. I wrote own:
const formatteddata=data.reduce((a1,a2)=>{
for (let t=0; t<a1.length; t++)
{var id1=a1[t].id
for (let tt=0; tt<a2.length; tt++)
{var id2=a2[tt].id
if(id1==date2)
{a1[t]={...a1[t],...a2[tt]}}
}
}
return a1
})
works with any amount of arrays of objects in arrays, with varying length and not always coinsciding dates

Categories

Resources