Filter object with keys in array - javascript

I have this array
const array = [1, 3, 6];
and this object
const obj = {
1: {id: 1, foo: 'a', ...},
2: {id: 2, foo: 'b', ...}
3: {id: 3, foo: 'c', ...},
4: {id: 4, foo: 'd', ...},
5: {...},
6: {...}
... // up to 1000 key/value paris
};
I wonder how to filter the obj with the keys in the array.
One way would be
obj.filter(elem => elem.id...);
But that iterates through all the elements in the obj, eventhough there are just three elements in the array.
Better would be to iterate over the array, but
array.filter(elem => elem === obj.id ...);
then only returns the elements from the array(meaning, 1, 3, 6).
What I need is an array looking like
const result = ['s', 'b', 'c', 'd'];
What is the best way to do this?

You can map over the array and and return the key foo from within the object
const res = array.map(elem => obj[elem].foo);
If all elements don't exists in the object you can add a filter condition to remove undefined values
const res = array.map(elem => obj[elem] &&obj[elem].foo).filter(Boolean);

If you have the id as key, you could map the wanted values by filtering the keys for getting only known key and then map the value.
const
array = [1, 3, 6],
object = { 1: { id: 1, foo: 'bar' }, 2: {}, 3: { id: 3, foo: 'foo' }, 4: {}, 5: {} },
result = array
.filter(Object.hasOwnProperty.bind(object))
.map(id => object[id].foo);
console.log(result);

If all the values in array exist in obj then use Array.map else if there are missing entries in obj then can use Array.reduce
const array = [1, 3, 6];
const obj = {1: {id: 1, foo: 'bar'},2: {id: 2, foo: 'foo'}};
const result = array.reduce((a,c) => obj[c] ? a.concat(obj[c].foo) : a, []);;
console.log(result);

You don't need to iterate all the objects you just need to check whether the key is exist or not, if the key is exists you push the foo value to the result array
Here's the code :
const array = [1, 3, 6];
const obj = {
1: {id: 1, foo: 'bar'},
2: {id: 2, foo: 'foo'},
3: {},
4: {},
5: {},
6: {}
};
let result = [];
array.forEach((el) => {
if(obj.hasOwnProperty(el)){
result.push(obj[el].foo)
}
});
console.log(result);

Related

From an array of objects, make an object that holds all values of identical keys

I've seen this problem explained when only needing the values of one key, but not how to do it when wanting the values of multiple keys.
The object has the following structure:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
I want my output to look like this:
newObj = [
{ foo: [1, 3, 5] },
{ bar: [2, 4, 6] },
];
Or even
newObj = [[1, 3, 5], [2, 4, 6]];
I've tried something like this:
const newObj = objArray.map(({ foo, bar }) => ( foo, bar ));
###Logic
Since you need to listen to keys in an object, which in turn is a part of an array, you need multiple loops.
Parent Loop: On array to get individual object
Child Loop: Loop on keys of current object
Using this, you can create a hashmap. If you use array here, you will have to search based on keys to push new value. hashmap reduces this effort.
At the end, you have a single object with key and values as an array. You can loop over keys and formulate your output
const objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
const result = objArray.reduce((acc, item) => {
Object.entries(item).forEach(([key, value]) => {
acc[key] = acc[key] || [];
acc[key].push(value)
})
return acc
}, {})
const finalValue = Object.entries(result).map(([key, value]) => ({ [key]: value }))
console.log(finalValue)
let objArray = [{
foo: 1,
bar: 2
}, {
foo: 3,
bar: 4
}, {
foo: 5,
bar: 6
}];
// 1. Get the keys
const keys = Object.keys(objArray[0]);
// Use the keys to read values from each object
const output = objArray.reduce((acc, curr) => {
keys.forEach(key => {
// if the key exists update, else initialize it
acc[key] = acc[key] ? acc[key].concat(curr[key]) : [curr[key]]
})
return acc
},{})
// Your outputs ow you want them.
console.log(output)
console.log(Object.values(output))

Change the array and return a new one

I have an array with objects. I filtered the array and returned the values that correspond with a condition. After that i created a new array what contains data from first. This array contains a value a which should overrides the first array.
const arr = [{
a: 1,
b: 2,
name: 'one'
},
{
a: 1,
b: 7,
name: 'two'
}
]
const b = [1, 2]
const res = arr.filter(i => b.includes(i.b))
const newArr = {
...res,
a: 'newdata'
}
console.log(newArr)
In console i got:
{
"0": {
"a": 1,
"b": 2,
"name": "one"
},
"a": "newdata"
}
But the expected output is:
{
"a": "newdata"
"b": 2,
"name": "one"
},
Why i get this and how to get the expected result?
After you filter the values you're interested in, you could use Array.map(..) to go through all the items and change their a property, here is an example:
const arr = [{
a: 1,
b: 2,
name: 'one'
},
{
a: 1,
b: 7,
name: 'two'
}
]
const b = [1, 2]
const res = arr.filter(i => b.includes(i.b)).map(i => ({...i, a: 'newdata'}));
console.log(res)
You simply spread an array and you get the indices from the array/object as key and the item/object as value.
Instead, you could take a function for a new object with additional key/value and map the filtered result by using the function.
const
arr = [{ a: 1, b: 2, name: 'one' }, { a: 1, b: 7, name: 'two' }],
b = [1, 2, 7],
res = arr.filter(i => b.includes(i.b)),
newObject = o => ({ ...o, a: 'newdata' }),
newArr = res.map(newObject);
console.log(newArr);

Compare two arrays containing objects including other objects, arrays, etc

Look at these examples of array comparative code:
// example-1
let array1 = ['a', 'b'];
let array2 = ['a', 'b'];
console.log(array1.equals(array2)); // returns true
// example-2
let array1 = ['a', 'b', 1];
let array2 = ['a', 'b', 1];
console.log(array1.equals(array2)); // returns true
// example-3
let array1 = ['a', 'b', {'a': 1, 'b': 2}];
let array2 = ['a', 'b', {'b': 2, 'a', 1}];
console.log(array1.equals(array2)); // returns false
I'm looking for a way to compare the arrays containing objects in them, but irrespective of the order of elements in a nested object, like mentioned in the example-3 above.
You should JSON.stringify() the arrays and compare them like so:
var arr1 = ['a', 'b', {'a': 1}];
var arr2 = ['a', 'b', {'a': 1}];
console.log(JSON.stringify(array1) == JSON.stringify(array2));
This works because it converts arrays of objects into a much simpler comparative state (JSON strings). This will only work if the arrays contain their properties in the same ordered in the OP's example.
underscore way:
_.isEqual(array1, array2)
You can use JSON.stringify() to get the JSON string and compare them with ===:
let array1 = ['a', 'b', {'a': 1}];
let array2 = ['a', 'b', {'a': 1}];
console.log(JSON.stringify(array1) === JSON.stringify(array2)); // returns true
You can just write a function the will recursively check until it gets down to primitives. For example:
function deepEqual(o1, o2){
if (Array.isArray(o1)) {
return Array.isArray(o2)
&& o1.length === o2.length
&& o1.every((item, idx) => deepEqual(item, o2[idx]))
}
if (typeof(o1) == 'object' && o1 != null){ // (typeof null == 'object)
return typeof(o2) == 'object'
&& o2 != null
&& deepEqual(Object.entries(o1)
.sort((a,b) => a[0].localeCompare(b[0])),Object.entries(o2).sort((a,b) => a[0].localeCompare(b[0])))
}
return o1 === o2
}
//Object order doesn't matter
let ob1 = [1, 2, {a: "test", b:"hello"}, 4]
let ob2 = [1, 2, {b:"hello", a: "test", }, 4]
console.log(deepEqual(ob1, ob2))
ob1 = [1, 2, {a: "test", b:"hello"}, 4]
ob2 = [1, 2, {b:"hello", a: "non-test", }, 4]
console.log(deepEqual(ob1, ob2))
// array order matters:
ob1 = [2, 1, {a: "test", b:"hello"}, 4]
ob2 = [1, 2, {b:"hello", a: "test", }, 4]
console.log(deepEqual(ob1, ob2))
console.log(deepEqual("test", "test"))
console.log(deepEqual(null, {a:"test"}))
// etc.

How can I uniquely union two array of objects?

I am trying to merge two arrays of objects without using the unionBy method from lodash.
Currently I have the following code working perfectly:
var array1 = [
{ a: 1, b: 'first'},
{ a: 2, b: 'second'}
];
var array2 = [
{ a: 3, b: 'third'},
{ a: 1, b: 'fourth'}
];
var array3 = __.unionBy(array2, array1, 'a');
This outputs:
[
{
"a": 3,
"b": "third"
},
{
"a": 1,
"b": "fourth"
},
{
"a": 2,
"b": "second"
}
]
This is the desired result but I can't use unionBy in my current work environment, so I'm looking for a result that uses either native JS or other lodash methods 3.6.0 or lower.
Concat and use Array#filter with a helper object to remove duplicates:
var array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];
var array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];
var result = array2.concat(array1).filter(function(o) {
return this[o.a] ? false : this[o.a] = true;
}, {});
console.log(result);
If ES6 is an option you can use a Set instead of the helper object:
const array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];
const array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];
const result = array2.concat(array1).filter(function(o) {
return this.has(o.a) ? false : this.add(o.a);
}, new Set());
console.log(result);
If you want to use an arrow function, you can't use the thisArg of Array.filter() to bind the Set as the this of the function (you can't bind this to arrow functions). You can use a closure instead (attribute for the method goes to #NinaScholz).
const array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];
const array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];
const result = [...array2, ...array1]
.filter((set => // store the set and return the actual callback
o => set.has(o.a) ? false : set.add(o.a)
)(new Set()) // use an IIFE to create a Set and store it set
);
console.log(result);
You could take a Set for filtering to get unique values.
var array1 = [{ a: 1, b: 'first' }, { a: 2, b: 'second' }],
array2 = [{ a: 3, b: 'third' }, { a: 1, b: 'fourth' }],
s = new Set,
array3 = array2.map(o => (s.add(o.a), o)).concat(array1.filter(o => !s.has(o.a)));
console.log(array3);
You can use an ES6 Map for this. Construct it with the data, keyed by the a property value, and then take the values out of the Map again:
var array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}],
array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];
var result = [...new Map([...array1,...array2].map( o => [o.a, o] )).values()];
console.log(result);
You can merge the 2 arrays and then filter the ones with same property a:
var array1 = [{ a: 1, b: 'first'},{ a: 2, b: 'second'}],
array2 = [{ a: 3, b: 'third'},{ a: 1, b: 'fourth'}],
array3 = [...array2, ...array1].filter((item, pos, arr) =>
arr.findIndex(item2 => item.a == item2.a) == pos);
console.log(array3)
If you want to still be able to specify the property by which to union you can implement you own function like this:
var array1 = [{ a: 1, b: 'first'},{ a: 2, b: 'second'}],
array2 = [{ a: 3, b: 'third'},{ a: 1, b: 'fourth'}],
array3 = unionBy(array1, array2, 'a');
function unionBy(array1, array2, prop){
return [...array2, ...array1].filter((item, pos, arr) =>
arr.findIndex(item2 => item[prop] == item2[prop]) == pos);
}
console.log(array3);
Note: One advantage of my answer over some of the answers is that it preserves the order like in lodash which may or may not be important.
ES5 using Array.filter and Array.find
var array1 = [{ a: 1, b: "first" }, { a: 2, b: "second" }];
var array2 = [{ a: 3, b: "third" }, { a: 1, b: "fourth" }];
function merge(a, b, prop) {
var reduced = a.filter(function(itemA) {
return !b.find(function(itemB) {
return itemA[prop] === itemB[prop];
});
});
return reduced.concat(b);
}
console.log(merge(array1, array2, "a"));
ES6 arrow functions
var array1 = [{ a: 1, b: "first" }, { a: 2, b: "second" }];
var array2 = [{ a: 3, b: "third" }, { a: 1, b: "fourth" }];
function merge(a, b, prop) {
const reduced = a.filter(
itemA => !b.find(itemB => itemA[prop] === itemB[prop])
);
return reduced.concat(b);
}
console.log(merge(array1, array2, "a"));
Another ES6 one line experiment
var array1 = [{ a: 1, b: "first" }, { a: 2, b: "second" }];
var array2 = [{ a: 3, b: "third" }, { a: 1, b: "fourth" }];
const merge = (a, b, p) => a.filter( aa => ! b.find ( bb => aa[p] === bb[p]) ).concat(b);
console.log(merge(array1, array2, "a"));
You could use ES6 find and reduce function smartly!
var array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];
var array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];
var res = array1.concat(array2).reduce((aggr, el)=>{
if(!aggr.find(inst=>inst.a==el.a))
return [...aggr, el];
else
return aggr
},[])
console.log(res);

Separate object with properties?

I have an object like
obj = {
a0: 1,
b0: 2,
b1: 3,
a1: 4
}
I want make this object to array as shown
obj1 = [
{a0: 1, b0: 2},
{a1: 4, b1: 3}
]
Group properties that end with same number. Please help me
You could use the number of the key as index and build a new object, if not exist. Then apply the value to the property.
var object = { a0: 1, b1: 3, a1: 4, c2: 5 },
grouped = Object.keys(object).reduce(function (result, key) {
var index = key.match(/\d+$/);
result[index] = result[index] || {};
result[index][key] = object[key];
return result;
}, []);
console.log(grouped);

Categories