Array of properties of objects in an array - javascript

Given the array of objects:
data = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}, {a: 7, b: 8, c: 9}]
How can I get an array of only a elements of the objects? Something like this:
a = [1, 4, 7]
Is it possible without iterating through the objects?

This is easy to achieve with .map()
var data = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}, {a: 7, b: 8, c: 9}];
var a = data.map(obj => obj.a);
console.log(a);

You can also try this:
var result = data.reduce(function(result, obj) {
return result.concat(obj.a);
}, []);

There are several methods.
You should look into Array.map() or the powerful Lodash library.
let aElements = data.map(o => o.a);

Using functional programming(map higher-order function):
var a = data.map(function(obj){return obj.a});
or the same in es6:
let a = data.map(obj=>obj.a);
But, nevertheless, technically Array.prototype.map iterates.

Related

How to unite 2 arrays with objects without repetition in JS

I have 2 objects.
const obj1 = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: 3}];
const obj2 = [{a: 1, b: 2}, {a: 3, b: 4}, {a: 8, b: 3}, {a: 7, b: 3}];
And I want to unite them in one but without repetitions.
the result should be obj3 = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 3, b: 4}, {a: 8, b: 3}, {a: 7, b: 3}];
const obj3 = obj1.map((item) => obj2.filter((i) => i !== item));
is not working.
You can always try to loop through the first one and loop through the second one for each entry, checking for repetition.
It's handy to use the lodash library to handle array operations like the one you're asking about:
https://lodash.com/docs/4.17.15#union
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.unionWith(objects, others, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
It's easy to add the lodash library to your code. You can download and save the lodash.js file here: https://lodash.com/, or just use a url from a CDN and include with your other scripts/script tags: https://cdnjs.com/libraries/lodash.js
The problem is you are mapping over an array, so basically you will do the filter every obj1.length time.
In term of the filter, filter will remove all the same item in obj1 and you will only get the not duplicate item in obj2, you could group them.
Another good solution I would suggest is to concat the two array and filter it.
const obj1 = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: 3}];
const obj2 = [{a: 1, b: 2}, {a: 3, b: 4}, {a: 8, b: 3}, {a: 7, b: 3}];
let obj3 = obj1.concat(obj2)
let obj4=obj3.filter((item,index) => obj3.indexOf(item) === index)
console.log(obj4)

ngFor data structure

There is somethign wrong with my data structure. I have resTest containing two object and keys such as pop,province, and address. I can think of couple options to do this but what would be easiest way to do this?
html
var columnsA = ["Pop","Province","Address"];
var columnsB = ["Pop","Province","Address"];
var res = {};
res['A'] = this.columns;
res['B'] = this.columns;
this.resTest = JSON.parse(JSON.stringify(getRes));
//this.resTest = {A: Array(24), B: Array(24)};
<table>
<thead>
<tr>
<th>RegionA</th>
<th>RegionB</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of this.resTest">
<td>{{item?.A}}</td>
<td>{{item?.B}}</td>
</tr>
</tbody>
</table>
error
ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables
I am expecting this data structure
{A :{…}, B: {…}}
This is what I have
{A: Array(24), B: Array(24)};
table
Since ngFor requires an Array, you JSON-parsed object needs to be refactored. This could be done via Object.keys and Array.prototype.map methods. Also, you should sort your array manually because Object.keys does not guarantee the order.
resTest = { A: [1, 2, 3], C: [3, 2, 1], B: [0, 0, 0] };
resTest = Object
.keys(resTest)
.sort((a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
})
.map(k => ({[k]: resTest[k]}));
// [{A: [1, 2, 3]}, {B: [0, 0, 0]}, {C: [3, 2, 1]}]
After that, you'll be able to pass resTest to the ngFor.
To convert your data into two-columns dataset, again, use Array.prototype.map:
resTest = { A: [1, 2, 3], B: [9, 8, 7] };
resTest = resTest['A']
.map((r, index) => ({ 'A': resTest['A'][index], 'B': resTest['B'][index] }));
// [{A: 1, B: 9}, {A: 2, B: 8}, {A: 3, B: 7}]
In this case you need to be sure, that A and B properties have the same length.

javascript pattern matching object

Given a javascript object array eg.
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
is there a faster way of getting all the b values from each object which meet a specific criteria such as a = 1 to return something like
b_consolidated = [2,3]
instead of looping through every object in the array?
You can use Array#filter function to get the items of your criteria, then use Array#map to get only b property.
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}];
let values = objArray.filter(item => item.a === 1).map(item => item.b);
console.log(values);
Or you can do this in one loop
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}];
let values = [];
objArray.forEach(item => {
if(item.a === 1) {
values.push(item.b);
}
});
console.log(values);
You could use Array#reduce in a single loop.
let array = [{ a: 1, b: 2, c: 3}, { a: 1, b: 3, c: 2 }, { a: 2, b: 5, c: 1 }],
result = array.reduce((r, o) => o.a === 1 ? r.concat(o.b) : r, []);
console.log(result);
Fastest version with for loop.
let array = [{ a: 1, b: 2, c: 3}, { a: 1, b: 3, c: 2 }, { a: 2, b: 5, c: 1 }],
i, l,
result = [];
for (i = 0, l = array.length; i < l; i++) {
if (array[i].a === 1) {
result.push(array[i].b);
}
}
console.log(result);
You only need to iterate over the array once, if you use reduce:
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
let result = objArray.reduce((arr, val) => {
if(val.a === 1)
arr.push(val.b);
return arr;
}, []);
console.log(result);
This is as fast as it'll get, short of a manual for loop:
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
let result = [];
for(var i = 0 ; i < objArray.length; i++){
if(objArray[i].a === 1)
result.push(objArray[i].b);
}
console.log(result);
Here's a JSPerf to illustrate the difference.
A manual for loop is by far the fastest.
More faster would be using .reduce
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}];
objArray.reduce(function(res,obj){
if(obj.a===1)
res.push(obj.b);
return res;
},[]);
// [2,3]
In Ramda
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
R.pipe(
R.filter(R.propEq('a', 1)),
R.pluck('b')
)(objArray)
// [2, 3]
Filter returns the array values matched by the condition.
Pluck returns a new list by plucking the same named property off all objects in the list supplied.
Edit 1:
Example of using the mentioned reduce pattern in Ramda:
R.reduce((acc, x) => R.ifElse(
R.propEq('a', 1),
(item) => R.pipe(R.prop('b'), R.append(R.__, acc))(item),
R.always(acc)
)(x), [])(objArray)
// [2, 3]

Lodash Not executing the nested method,

given this array var array = [{a:1, b: 2, c: 3}, {a: 2, b: 4, c: 4}].
I want to iterate through each item in the array and omit the key c and it's value using lodash.
here is the method I tried
_.each(array, function (obj) {
return _.omit(obj, ['a']);
});
Expected Output should be // [{b: 2, c: 3}, {b: 4, c: 4} ]
but lodash returns the original array// [{a:1, b: 2, c: 3}, {a: 2, b: 4, c: 4}]
Instead of forEach() to return new modified array you should use map(), also first parameter is array.
var array = [{a:1, b: 2, c: 3}, {a: 2, b: 4, c: 4}]
var result = _.map(array, function (obj) {
return _.omit(obj, ['a']);
});
console.log(JSON.stringify(result, 0, 4))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
A probably-not-the-lo-dash-iest way of handling it (assuming you actually want to omit a):
_.each(lnkn, function (obj) {
var returnObj = {};
for (prop in obj) {
if (obj.hasOwnProperty(prop) {
if (prop !== 'a') {
returnObj[prop] = obj[prop];
}
}
}
return returnObj;
});
_.forEach is supposed to return the original array. You re looking for _.map.
const array = [{a:1, b: 2, c: 3}, {a: 2, b: 4, c: 4}];
const result = _.map(array, obj => _.omit(obj, 'a'));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Or you can modify the objects in place:
const array = [{a:1, b: 2, c: 3}, {a: 2, b: 4, c: 4}];
_.forEach(array, obj => {
delete obj.a;
});
console.log(array);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

What's the right way to sort the child object by their property in a Map in Immutable.js

I have an Map m:
var m = Immutable.fromJS({
first: {a: 6, b: 3},
second: {a: 3, b: 6},
third: {a: 2, b: 4}
})
I want to get the child objects in m sorted by their property b, like this:
[
{a: 6, b: 3},
{a: 2, b: 4},
{a: 3, b: 6}
]
I have tried below:
m.valueSeq().sort(function(a, b) {
return a.get('b') > b.get('b')
}).toJS()
It works well in Chrome and node.js, but in Safari v8.0 in OS X, the result is
[
{a: 6, b: 3},
{a: 3, b: 6},
{a: 2, b: 4}
]
It did not sort at all! This made some bugs in my React/Redux app. What's the matter of it? And what is the right way to sort it? Thanks!
Your custom compare function requires the returning of 1, -1 or 0 in order to sort the items correctly.
m.valueSeq().sort(function(a, b) {
if (a.get('b') > b.get('b')) return 1;
if (a.get('b') < b.get('b')) return -1;
else return 0;
}).toJS();

Categories