Get missing objects from 2 arrays of objects - javascript

let selected = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'}
];
let all = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'},
{id: 32, name: 'United States'},
{id: 40, name: 'China'}
]
How do I get non-selected countries from all objects and print it out in another variable? Based on id key of those which are in selected array?

You need to find all objects that aren't contained in selected and then do something with them:
let nonSelectedItems = all.filter(obj => selected.every(s => s.id !== obj.id));
//do stuff with non-selected items

You can use filter and find, so as soon as element with same id is found in selected it will filter out that element from all. You can also use some instead of find.
let selected = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'}
];
let all = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'},
{id: 32, name: 'United States'},
{id: 40, name: 'China'}
]
var r = all.filter(e => !selected.find(a => e.id === a.id));
console.log(r)

Generate an object which holds id as a property using Array#reduce method(which helps to speed up since you need to iterate over and over) and use Array#filter method to filter elements from all array.
// generate the object reference
let ref = selected.reduce(function(obj, o) {
// define property
obj[o.id] = true;
// return object property
return obj;
// set initial value as an object
}, {});
// filter out array elements
let res = all.filter(function(o) {
return !ref[o.id]
})
let selected = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}];
let all = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}, {
id: 32,
name: 'United States'
}, {
id: 40,
name: 'China'
}]
let ref = selected.reduce(function(obj, o) {
obj[o.id] = true;
return obj;
}, {});
console.log(
all.filter(function(o) {
return !ref[o.id]
})
)
With ES6 arrow function :
let ref = selected.reduce((obj, o) => (obj[o.id] = true, obj), {});
let res = all.filter(o => !ref[o.id]);
let selected = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}];
let all = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}, {
id: 32,
name: 'United States'
}, {
id: 40,
name: 'China'
}]
let ref = selected.reduce((obj, o) => (obj[o.id] = true, obj), {});
console.log(
all.filter(o => !ref[o.id])
)

Related

Copy values from multiple keys from array of object

I want to copy value of name and age into another array, below code is working fine, But I wanted to know better way to do it.
const users = [
{ id: 0, name: 'John', age:34 },
{ id: 1, name: 'Wayne', age:44 },
{ id: 2, name: 'David', age:24 },
];
let values=[];
users && users.map(user => {
values.push(user['name'])
values.push(user['age'])
})
console.log(values);
output
['John', 34, 'Wayne', 44, 'David', 24]
You can bind each items to an array containing both its name and age and then flattern these arrays.
This can be done using Array#FlatMap
const users = [
{ id: 0, name: 'John', age:34 },
{ id: 1, name: 'Wayne', age:44 },
{ id: 2, name: 'David', age:24 },
];
const nameAndAges = users.flatMap(user => [user.name, user.age])
console.log(nameAndAges)
Your solution looks good, this can be also solved in different ways, I guess you may want to create function for handling this.
const users = [
{ id: 0, name: 'John', age: 34 },
{ id: 1, name: 'Wayne', age: 44 },
{ id: 2, name: 'David', age: 24 },
];
const result = mapArrayToProps(users, ['name', 'age']);
function mapArrayToProps(arr, props) {
return arr.flatMap(obj => mapObjectToProps(obj, props));
}
function mapObjectToProps(obj, props) {
return props.map(prop => obj[prop])
}
console.log(result);

How to pull nested object based on string match?

activePath would change dynamically based on api call , how to pull object based on the activePath string that matches in nested object ?
path examples : Drug/GetRefills in this case it should push data.Drug.getRefills and if path is Payment/getAccount it should push data.Payment.getAccount
main.js
const data =
[{
id: 11,
name: "Drug",
children: [{
id: 12,
name: "getRefills"
}, {
id: 13,
name: "getDetails"
}]
}, {
id: 14,
name: "Payment",
children: [{
id: 15,
name: "getAccount"
}, {
id: 16,
name: "getAccountDetails"
}]
}]
function getModelData(data){
var activePath = "Drug/GetRefills";
var _interfaces = [];
$.each(data, function(id, item){
if (activePath.toLowerCase().includes(item.name)) {
console.log('OBJ', item);
_interfaces.push(item); // it should push getrefills object into interfaces
}
});
return _interfaces;
}
You can use recursion to find the object (similar to DFS):
const data = [{
id: 11,
name: "Drug",
children: [{
id: 12,
name: "getRefills"
}, {
id: 13,
name: "getDetails"
}]
}, {
id: 14,
name: "Payment",
children: [{
id: 15,
name: "getAccount"
}, {
id: 16,
name: "getAccountDetails"
}]
}];
function getModelData(path) {
function find(arr, [key, ...rest]) {
const obj = arr.find(o => o.name === key);
if (!obj) return null;
return rest.length ? find(obj.children || [], rest) : obj;
}
return find(data, path.split('/'));
// Instead of returning, add the found object to _interfaces
}
console.log(getModelData('Drug/getRefills'));
console.log(getModelData('Drug/getRefillsss'));
console.log(getModelData('Payment/getAccountDetails'));
I think you are looking for some util like flat. Here is the very basic example.
const data = [
{
id: 11,
name: "Drug",
children: [
{
id: 12,
name: "getRefills"
},
{
id: 13,
name: "getDetails"
}
]
},
{
id: 14,
name: "Payment",
children: [
{
id: 15,
name: "getAccount"
},
{
id: 16,
name: "getAccountDetails"
}
]
}
];
function flat(array) {
return array.reduce((m, {name, children}) => {
children.forEach((child) => {
const {name:cname} = child
const fullName = `${name.toLowerCase()}/${cname.toLowerCase()}`
if(!m[fullName]) m[fullName] =[]
m[fullName].push(child)
})
return m
},{})
}
function getModelData(path, data) {
return flat(data)[path.toLowerCase()];
}
var activePath = "Drug/GetRefills";
console.log(getModelData(activePath, data));
//Output
[ { id: 12, name: 'getRefills' } ]
Here is an example how you can get the right object from the data with the filter function from lodash.
const activePath = "Drug/getRefills";
// get name
let name = activePath.substr(0, activePath.indexOf('/'));
// get detail
let detail = activePath.substr(activePath.indexOf('/') + 1);
const data =
[{
id: 11,
name: "Drug",
children: [{
id: 12,
name: "getRefills"
}, {
id: 13,
name: "getDetails"
}]
}, {
id: 14,
name: "Payment",
children: [{
id: 15,
name: "getAccount"
}, {
id: 16,
name: "getAccountDetails"
}]
}]
// Get data with lodash filter
const currentName = _.filter(data, d => d.name === name);
const currentDetail = _.filter(currentName[0].children, c => c.name === detail);
console.log(currentDetail[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
const data = [{
id: 11,
name: "Drug",
children: [{
id: 12,
name: "getRefills"
}, {
id: 13,
name: "getDetails"
}]
}, {
id: 14,
name: "Payment",
children: [{
id: 15,
name: "getAccount"
}, {
id: 16,
name: "getAccountDetails"
}]
}];
function getModelData(data,activePath){
var activePaths = activePath.split("/"),
_interfaces = [];
$.each(data, function(index, item){
if(!item.children || item.children.length == 0){
return false; //break
}
if(activePaths[0].toLowerCase() != item.name.toLowerCase()){
return true;//continue
}
childs = item.children;
$.each(childs,function(name,item){
item.name.toLowerCase() === activePaths[1].toLowerCase() && _interfaces.push(item);
});
});
return _interfaces;
}
console.log(getModelData(data,'Drug/getRefills'));
console.log(getModelData(data,'Payment/getAccountDetails'));

How can I remove the name field from a json array?

I have a json array like this:
(3) [{…}, {…}, {…}]
0: {Id: 1, Name: "bask"}
1: {Id: 2, Name: "voll"}
2: {Id: 3, Name: "badminton"}
I want to turn it into something like this:
{1:"bask",2:"voll",3:"badminton"}
You can use reduce to loop through array and build a object of desired key/value pair
let data = [{Id: 1, Name: "bask"},{Id: 2, Name: "voll"},{Id: 3, Name: "badminton"}]
let output = data.reduce((op, {Id, Name}) => {
op[Id] = Name
return op
},{})
console.log(output)
You could take Object.fromEntries with the maped key/value pairs.
var array = [{ Id: 1, Name: "bask" }, { Id: 2, Name: "voll" }, { Id: 3, Name: "badminton" }],
object = Object.fromEntries(array.map(({ Id, Name }) => [Id, Name]));
console.log(object);
You can check out the reduce() function!
let array = [
{Id: 1, Name: "bask"},
{Id: 2, Name: "voll"},
{Id: 3, Name: "badminton"}
];
console.log(_.reduce(array, function(result, obj){
result[obj.Id] = obj.Name;
return result;
}, {}));
You can checkout lodash an awesome library with many other such utilities!
You can do this with reduce():
var a = [
{Id: 1, Name: "bask"},
{Id: 2, Name: "voll"},
{Id: 3, Name: "badminton"}
]
b = a.reduce((acc, item) => {
acc[item.Id] = item.Name;
return acc;
}
console.log(b);
You can do it in different ways, here one of them.
let dataArray = [
{id: 1, name: 'bask'},
{id: 2, name: 'voll'},
{id: 3, name: 'badminton'}
]
let ouputObject = {}
dataArray.map(data => {
ouputObject[`${data.id}`] = data.name
})
console.log(ouputObject)
outputObject will be
Object {
1: "bask",
2: "voll",
3: "badminton"
}
Using Array.reduce() :
var arr = [{
Id: 1,
Name: "bask"
}, {
Id: 2,
Name: "voll"
}, {
Id: 3,
Name: "badminton"
}];
var reduceObj = arr.reduce(function(result, currentElement) {
result[currentElement.Id] = currentElement.Name;
return result;
}, {});
console.log(reduceObj);
Using Array.map() :
var arr = [{
Id: 1,
Name: "bask"
}, {
Id: 2,
Name: "voll"
}, {
Id: 3,
Name: "badminton"
}];
var mapObject = {}
arr.map(obj => {
mapObject[obj.Id] = obj.Name
})
console.log(mapObject);

Why does map function return undefined but console.log logs out?

I want to return matching proprieties of two arrays of objects. But I got undefined from map function.
let fruits1 = [
{id: 1, name: "apple"},
{id: 2, name: "dragon fruit"},
{id: 3, name: "banana"},
{id: 4, name: "kiwi"},
{id: 5, name: "pineapple"},
{id: 6, name: "watermelon"},
{id: 7, name: "pear"},
]
let fruits2 = [
{id: 7, name: "pear"},
{id: 10, name: "avocado"},
{id: 5, name: "pineapple"},
]
fruits1.forEach((fruit1) => {
fruits2.filter((fruit2) => {
return fruit1.name === fruit2.name;
}).map((newFruit) => {
//console.log(newFruit.name);
return newFruit.name;
})
})
What are you looking for is an array intersection:
// Generic helper function that can be used for the three operations:
const operation = (list1, list2, isUnion = false) =>
list1.filter( a => isUnion === list2.some( b => a.name === b.name ) );
// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
inFirstOnly = operation,
inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);
Usage:
console.log('inBoth:', inBoth(list1, list2));
Working Example:
// Generic helper function that can be used for the three operations:
const operation = (list1, list2, isUnion = false) =>
list1.filter( a => isUnion === list2.some( b => a.name === b.name ) );
// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
inFirstOnly = operation,
inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);
let fruits1 = [
{id: 1, name: "apple"},
{id: 2, name: "dragon fruit"},
{id: 3, name: "banana"},
{id: 4, name: "kiwi"},
{id: 5, name: "pineapple"},
{id: 6, name: "watermelon"},
{id: 7, name: "pear"},
]
let fruits2 = [
{id: 7, name: "pear"},
{id: 10, name: "avocado"},
{id: 5, name: "pineapple"},
]
console.log('inBoth:', inBoth(fruits1, fruits2));
You could use a Set and filter the names.
const names = ({ name }) => name;
var fruits1 = [{ id: 1, name: "apple" }, { id: 2, name: "dragon fruit" }, { id: 3, name: "banana" }, { id: 4, name: "kiwi" }, { id: 5, name: "pineapple" }, { id: 6, name: "watermelon" }, { id: 7, name: "pear" }],
fruits2 = [{ id: 7, name: "pear" }, { id: 10, name: "avocado" }, { id: 5, name: "pineapple" }],
common = fruits1
.map(names)
.filter(Set.prototype.has, new Set(fruits2.map(names)));
console.log(common);
What you want to do is this:
/* first we filter fruits1 (arbitrary) */
let matchingFruits = fruits1.filter(f1 => {
/* then we filter the frut if it exists in frtuis2 */
return fruits2.find(f2 => f2.name === f1.name)
}).map(fruit => fruit.name) // and now we map if we only want the name strings
If you're not using a polyfill Array.find will not work in IE. The alternative would be using Array.indexOf (thanks for pointing this out #JakobE).
Be aware that Array.forEach return value is undefined and that, in order to actually use the Array.map correctly, one has to consume the returned value somehow or assign it to a variable, as we just did with matchingFruits.

Merging/extend javascript object arrays based on join of a key property in each

I am wanting to merge the following object arrays, by first joining on the id property
var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];
var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];
So the result would be
[{
id: 1,
name: 'fred',
title: 'boss',
wage: '300',
rate: 'day'
},{
id: 2,
name: 'jim',
title: 'nobody',
wage: '10',
rate: 'hour'
},{
id: 3,
name: 'bob',
title: 'dancer',
wage: '500',
rate: 'week'
}]
I would like to avoid using js frameworks (if possible), although ExtJs is already part of the project.
AT the moment I have a loop with an inner loop that if the keys match it copies the properties and breaks out of the inner loop to start the next outer loop.
Any better suggestions?
Like this?
var combined = [];
function findSecond(id,second){
for (var i=0;i<second.length;i++){
if(second[i].id === id){
return second[i];
}
}
return null
}
while (el = arr1.pop()){
var getSec = findSecond(el.id,arr2);
if (getSec){
for (var l in getSec){
if (!(l in el)) {
el[l] = getSec[l];
}
}
combined.push(el);
}
}
If the arrays have the same length, and the id's are equal, a simpler merge will do:
function merge(a1,a2) {
var i = -1;
while ((i = i+1)<a1.length) {
for (var l in a2[i]) {
if (!(l in a1[i] )) {
a1[i][l] = a2[i][l];
}
}
}
return a1;
}
Here's a working example
[Edit 2016/07/30] Added a snippet using more functional approach and, based on #djangos comment, an extra method to combine both arrays.
(function() {
var alert = function(str) {document.querySelector('#result').textContent += str + '\n';};
var arrays = getArrays();
alert('Combine on id (shared id\'s):')
alert(JSON.stringify(combineById(arrays.arr1, arrays.arr2), null, ' '));
alert('\nCombine on id (all id\'s):')
alert(JSON.stringify(combineBothById(arrays.arr1, arrays.arr2), null, ' '));
// for combineBothById the parameter order isn't relevant
alert('\nCombine on id (all id\'s, demo parameter order not relevant):')
alert(JSON.stringify(combineBothById(arrays.arr2, arrays.arr1), null, ' '));
// combine first array with second on common id's
function combineById(arr1, arr2) {
return arr1.map(
function (el) {
var findInB = this.filter(function (x) {return x.id === el.id;});
if (findInB.length) {
var current = findInB[0];
for (var l in current) {
if (!el[l]) {el[l] = current[l];}
}
}
return el;
}, arr2);
}
// combine first array with second on all id's
function combineBothById(arr1, arr2) {
var combined = arr1.map(
function (el) {
var findInB = this.filter(function (x) {return x.id === el.id;});
if (findInB.length) {
var current = findInB[0];
for (var l in current) {
if (!el[l]) {el[l] = current[l];}
}
}
return el;
}, arr2);
combined = combined.concat(arr2.filter(
function (el) {
return !this.filter(function (x) {return x.id === el.id;}).length;
}, combined));
return combined;
}
function getArrays() {
return {
arr1: [{
id: 1,
name: 'fred',
title: 'boss'
}, {
id: 2,
name: 'jim',
title: 'nobody'
}, {
id: 3,
name: 'bob',
title: 'dancer'
}],
arr2: [{
id: 1,
wage: '300',
rate: 'day'
}, {
id: 2,
wage: '10',
rate: 'hour'
}, {
id: 4,
wage: '500',
rate: 'week'
}]
};
}
}());
<pre id="result"></pre>
You can merge two arrays by id column with Alasql library:
var res = alasql('SELECT * FROM ? arr1 JOIN ? arr2 USING id', [arr1,arr2]);
Try this example at jsFiddle.
try this...
var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];
var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];
let arr5 = arr1.map((item, i) => Object.assign({}, item, arr2[i]));
console.log(arr5)

Categories

Resources