Object Array to Hash Table - javascript

Would like to convert (using Ramda)
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
into
var b = {1:'one', 2:'two', 3:'three'}
I'm very new to functional programming and I am open to any ideas.
What I think we must do is, use a reduce function starting with {} and then we want to add on each iteration the id and name of the current element to the hashtable. This is what I came up with which appears very wrong. Am I close?
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'},{id: 4, name: 'four'} ]
var b = R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
b

There are many approaches to this that will work. All the other correct answers helps show that. I'll list a few of my own below. But first,
Why your approach isn't working
You try to do this:
R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
It's clear what you're trying to do here. The trouble is that R.prop('id') and R.prop('name') are functions. R.assoc does not accept functions; it wants a String (Number will actually serve) and an arbitrary value. So assoc will not work with these in this manner.
One attempt to clean this up is to recognize that functions can be thought of as containers of values. In some -- perhaps surprising, but quite useful -- way, a function is a container of its return value. R.lift is meant to turn functions that work on values into ones that work on containers of values. It works like this: R.multiply accepts numbers. If we lift multiply, the resulting function accepts containers of numbers. Like this:
R.lift(R.multiply)(Maybe.Just(5), Maybe.Just(3)) // Maybe.Just(15)
If we supply lift(multiply) with two functions, then we get back a function that returns the result of multiplying their return values:
const area = R.lift(R.multiply)(prop('width'), prop('height'))
area({width: 5, height: 3})
So perhaps we could update your technique with a lift:
R.reduce(R.lift(R.assoc)(R.prop('id'), R.prop('name'), identity), {}, a)
Unfortunately, this fails again. The trouble this time is that reduce takes a binary callback, supplying both the accumulator and the current value. But R.prop('id') and R.prop('name') don't recognize that. They look for the relevant properties on the accumulator, which simply are not there.
We might still be able to fix this, but at this point, we'd be losing a great deal of the simplicity of this solution. So let's look at other possibilities.
Some solutions
Simple Reduce
Each of these two versions uses a simple reduce call, as your solution tried to do:
const convertToHash = reduce((acc, {id, name}) => merge(acc, {[id]: name}), {})
and
const convertToHash = reduce((acc, {id, name}) => ({...acc, [id]: name}), {})
They are much the same. Both create disposable objects on each iteration. In the first one, you could replace R.merge with Object.assign without any real issues, as the resulting mutation is internal to the function. The second one seems slightly more elegant, but it rebuilds the entire accumulator on each iteration. As the engine optimization for ES6 proceeds, this will likely eventually not be a performance problem, but it might be right now, especially if this is in performance-critical code.
Using zipWith
Ramda's zip functions take two lists and combine them, position by position, into a single list. R.zipWith accepts a function used to combine the two elements into one. Here we use R.objOf, which turns a name and a value into a single-property object. (objOf('foo', 42) //=> {foo: 42}.):
const convertToHash = compmose(mergeAll, lift(zipWith(objOf))(pluck('id'), pluck('name')))
As above, we use lift to make this zipWith(objOf) work with functions. That results in something like [{"1": "one"}, {"2": "two"}, {"3": "three"}], which we then pass to R.mergeAll to create a single object.
Using props and fromPairs
This solutions uses R.props and R.fromPairs. fromPairs accepts a list of name-value pairs (as two-element arrays) and turns them into a single object. props pulls the named properties into a stand-alone array. Mapping this over the original list give us the input to fromPairs:
const convertToHash = compose(fromPairs, map(props(['id', 'name'])))
Although I'm fond of the zipWith solution, and would use it if its output was what I wanted, having to add the mergeAll, makes it harder to comprehend at a glance. And so this solution wins in my mind as the best Ramda choice.

You could achieve this by the following:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
var b = R.reduce((dict, item) => ({ ...dict, [ item.id ] : item.name }), {}, a)
This approach uses es6 syntax to add keys (named via item.id) with value (item.name) to your resulting dictionary, during each iteration of the reduction.

You can create a pipeline (R.pipe) to convert your array of objects to a hash:
Get the values of each (R.map) objects' properties (R.props).
Convert the array of pairs to an object (R.fromPairs).
const a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
const convertToHash = (props) =>
R.pipe(
R.map(R.props(props)),
R.fromPairs
);
const result = convertToHash(['id', 'name'])(a);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Try using this code:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
R.map(i=> {
var n = []
n[R.keys(i)[0]] = i.name
return n
},a)

when using es6 you can get a more flexible solution
let a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
let out = a.reduce((acc, {id, name}) => {
acc[id] = name;
return acc;
}, {});
console.log(out)

You can loop your initial array and assign to new object:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
var new_a = {};
a.forEach(function(e) {
new_a[e.id] = e.name;
});
console.log(new_a);

Related

Angular compare array and update one of it

I have this two sets of array, I need to compare them and if the object from 2th array found on 1st array, I need to update the status on 1st array to true. On angular how to massage the data, thanks!
Below are sample of array,
arr1 = [
{id: 1, status: false},
{id: 2, status: false},
{id: 3, status: false}
];
arr2 = [
{id: 4},
{id: 5},
{id: 2}
];
Even though you have not provided any (faulty) code from yourself, I understand that it sometimes can be difficult to even begin with such a thing.
In this case, you should find a beginning point. You can use the source array (arr1) as starting point, or arr2. If arr2 is tiny, it's quicker to use this as starting point. The bigger arr2 gets, the more you might want to start with arr1, because that solution is more readable (subjective).
With arr2 as starting point, you can use the forEach method, in combination with find:
arr2.forEach((item) => {
const target = arr1.find(({ id }) => item.id === id);
if (target) {
target.status = true;
}
});
With the arr1 as starting point, you can use the map method. This will make a new array in place, which is usually a good thing to do, considering angular's change detection system:
arr1 = arr1.map((item) => ({
...item,
status: arr2.some(({ id }) => item.id === id)
});
This will also create a new object reference. Depending on your data set, performance might be an issue, but this will only be noticable if you are processing +10.000 objects (depending on the complexity of your objects).
If you do not want a status: true be overwritten to false on arr1, you need to add an extra condition:
arr1 = arr1.map((item) => ({
...item,
status: item.status || arr2.some(({ id }) => item.id === id)
});

Javascript -Turn the values of the properties of the javascript objects to indexed array [duplicate]

I have JavaScript object array with the following structure:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
I want to extract a field from each object, and get an array containing the values, for example field foo would give array [ 1, 3, 5 ].
I can do this with this trivial approach:
function getFields(input, field) {
var output = [];
for (var i=0; i < input.length ; ++i)
output.push(input[i][field]);
return output;
}
var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]
Is there a more elegant or idiomatic way to do this, so that a custom utility function would be unnecessary?
Note about suggested duplicate, it covers how to convert a single object to an array.
Here is a shorter way of achieving it:
let result = objArray.map(a => a.foo);
OR
let result = objArray.map(({ foo }) => foo)
You can also check Array.prototype.map().
Yes, but it relies on an ES5 feature of JavaScript. This means it will not work in IE8 or older.
var result = objArray.map(function(a) {return a.foo;});
On ES6 compatible JS interpreters you can use an arrow function for brevity:
var result = objArray.map(a => a.foo);
Array.prototype.map documentation
Speaking for the JS only solutions, I've found that, inelegant as it may be, a simple indexed for loop is more performant than its alternatives.
Extracting single property from a 100000 element array (via jsPerf)
Traditional for loop 368 Ops/sec
var vals=[];
for(var i=0;i<testArray.length;i++){
vals.push(testArray[i].val);
}
ES6 for..of loop 303 Ops/sec
var vals=[];
for(var item of testArray){
vals.push(item.val);
}
Array.prototype.map 19 Ops/sec
var vals = testArray.map(function(a) {return a.val;});
TL;DR - .map() is slow, but feel free to use it if you feel readability is worth more than performance.
Edit #2: 6/2019 - jsPerf link broken, removed.
Check out Lodash's _.pluck() function or Underscore's _.pluck() function. Both do exactly what you want in a single function call!
var result = _.pluck(objArray, 'foo');
Update: _.pluck() has been removed as of Lodash v4.0.0, in favour of _.map() in combination with something similar to Niet's answer. _.pluck() is still available in Underscore.
Update 2: As Mark points out in the comments, somewhere between Lodash v4 and 4.3, a new function has been added that provides this functionality again. _.property() is a shorthand function that returns a function for getting the value of a property in an object.
Additionally, _.map() now allows a string to be passed in as the second parameter, which is passed into _.property(). As a result, the following two lines are equivalent to the code sample above from pre-Lodash 4.
var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
_.property(), and hence _.map(), also allow you to provide a dot-separated string or array in order to access sub-properties:
var objArray = [
{
someProperty: { aNumber: 5 }
},
{
someProperty: { aNumber: 2 }
},
{
someProperty: { aNumber: 9 }
}
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
Both _.map() calls in the above example will return [5, 2, 9].
If you're a little more into functional programming, take a look at Ramda's R.pluck() function, which would look something like this:
var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray)
Example to collect the different fields from the object array
let inputArray = [
{ id: 1, name: "name1", value: "value1" },
{ id: 2, name: "name2", value: "value2" },
];
let ids = inputArray.map( (item) => item.id);
let names = inputArray.map((item) => item.name);
let values = inputArray.map((item) => item.value);
console.log(ids);
console.log(names);
console.log(values);
Result :
[ 1, 2 ]
[ 'name1', 'name2' ]
[ 'value1', 'value2' ]
It is better to use some sort of libraries like lodash or underscore for cross browser assurance.
In Lodash you can get values of a property in array by following method
_.map(objArray,"foo")
and in Underscore
_.pluck(objArray,"foo")
Both will return
[1, 2, 3]
Using Array.prototype.map:
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
See the above link for a shim for pre-ES5 browsers.
In ES6, you can do:
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
If you want multiple values in ES6+ the following will work
objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];
let result = objArray.map(({ foo, baz }) => ({ foo, baz }))
This works as {foo, baz} on the left is using object destructoring and on the right side of the arrow is equivalent to {foo: foo, baz: baz} due to ES6's enhanced object literals.
While map is a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined.
I'd opt for a reduce solution, which can simply ignore the property or even set you up with a default value.
function getFields(list, field) {
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// check if the item is actually an object and does contain the field
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would work even if one of the items in the provided list is not an object or does not contain the field.
It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.
function getFields(list, field, otherwise) {
// reduce the provided list to an array containing either the requested field or the alternative value
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a map is slightly cheaper than a reduce):
function getFields(list, field, otherwise) {
// map the provided list to an array containing either the requested field or the alternative value
return list.map(function(item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
return typeof item === 'object' && field in item ? item[field] : otherwise;
}, []);
}
jsbin example
And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.
function getFields(list, field, otherwise) {
// determine once whether or not to use the 'otherwise'
var alt = typeof otherwise !== 'undefined';
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
else if (alt) {
carry.push(otherwise);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concat function.
function getFields(list, field, otherwise) {
var alt = typeof otherwise !== 'undefined';
return list.reduce(function(carry, item) {
return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
}, []);
}
jsbin example
The above answer is good for a single property but when select multiple properties from an array use this
var arrayObj=[{Name,'A',Age:20,Email:'a.gmail.com'},{Name,'B',Age:30,Email:'b.gmail.com'},{Name,'C',Age:40,Email:'c.gmail.com'}]
now I select only two fields
var outPutArray=arrayObj.map(( {Name,Email} ) => ({Name,Email}) )
console.log(outPutArray)
If you want to also support array-like objects, use Array.from (ES2015):
Array.from(arrayLike, x => x.foo);
The advantage it has over Array.prototype.map() method is the input can also be a Set:
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
In general, if you want to extrapolate object values which are inside an array (like described in the question) then you could use reduce, map and array destructuring.
ES6
let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);
console.log(b)
The equivalent using for in loop would be:
for (let i in a) {
let temp = [];
for (let j in a[i]) {
temp.push(a[i][j]);
}
array.push(temp);
}
Produced output: ["word", "again", "some", "1", "2", "3"]
If you have nested arrays you can make it work like this:
const objArray = [
{ id: 1, items: { foo:4, bar: 2}},
{ id: 2, items: { foo:3, bar: 2}},
{ id: 3, items: { foo:1, bar: 2}}
];
let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
console.log(result)
Easily extracting multiple properties from array of objects:
let arrayOfObjects = [
{id:1, name:'one', desc:'something'},
{id:2, name:'two', desc:'something else'}
];
//below will extract just the id and name
let result = arrayOfObjects.map(({id, name}) => ({id, name}));
result will be [{id:1, name:'one'},{id:2, name:'two'}]
Add or remove properties as needed in the map function
In ES6, in case you want to dynamically pass the field as a string:
function getFields(array, field) {
return array.map(a => a[field]);
}
let result = getFields(array, 'foo');
It depends on your definition of "better".
The other answers point out the use of map, which is natural (especially for guys used to functional style) and concise. I strongly recommend using it (if you don't bother with the few IE8- IT guys). So if "better" means "more concise", "maintainable", "understandable" then yes, it's way better.
On the other hand, this beauty doesn't come without additional costs. I'm not a big fan of microbench, but I've put up a small test here. The results are predictable, the old ugly way seems to be faster than the map function. So if "better" means "faster", then no, stay with the old school fashion.
Again this is just a microbench and in no way advocating against the use of map, it's just my two cents :).
create an empty array then forEach element from your list, push what you want from that object into your empty array.
let objArray2 = [];
objArray.forEach(arr => objArray2.push(arr.foo));
From an array of objects, extract the value of a property as an array with for loop.
//input
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
//Code
let output=[];
for(let item of objArray){
output.push(item.foo);
}
// Output
[ 1, 3, 5 ]
Above provided answer is good for extracting single property, what if you want to extract more than one property from array of objects.
Here is the solution!!
In case of that we can simply use _.pick(object, [paths])
_.pick(object, [paths])
Lets assume objArray has objects with three properties like below
objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];
Now we want to extract foo and bar property from every object and store them in a separate array.
First we will iterate array elements using map and then we apply Lodash Library Standard _.pick() method on it.
Now we are able to extract 'foo' and 'bar' property.
var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])})
console.log(newArray);
and result would be
[{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]
enjoy!!!
Here is another shape of using map method on array of objects to get back specific property:
const objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
const getProp = prop => obj => obj[prop];
const getFoo = getProp('foo');
const fooes = objArray.map(getFoo);
console.log(fooes);
I would only improve one of the answers if you even don't know the exact property of the object you'r playing with use below:
let result = objArray.map(a => a[Object.getOwnPropertyNames(a)]);
Function map is a good choice when dealing with object arrays. Although there have been a number of good answers posted already, the example of using map with combination with filter might be helpful.
In case you want to exclude the properties which values are undefined or exclude just a specific property, you could do the following:
var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
https://jsfiddle.net/ohea7mgk/
Destructure and get specific attributes from array of object:
const customerList = dealerUserData?.partyDetails.map(
({ partyId, custAccountId }) => ({
partyId,
custAccountId,
customerId: dealerUserData?._id,
userId: dealerUserData?.authUserID,
}),
);

Select certain values and sort alphabetically in ES6

How can I select on certain values and sort them alphabetically using ES6.
The current codes defines a constant. I wanted to know if I could manipulate this data without having to create a new constant with just the values I need.
export const names = {
John: 'John',
Mike: 'Mike',
David: 'David',
Chris: 'Chris'
};
Return only Chris and David.
Chris
David
const names = {
John: 'John',
Mike: 'Mike',
David: 'David',
Chris: 'Chris',
};
const searchNames = (args, search) => {
const arr = Object.values(args);
const result = arr.filter(n => {
return search.includes(n);
});
return result.sort().join('\n');
};
searchNames(names, ['David', 'Chris']);
// returns: Chris
// David
Not the most elegant, but it works. It would be easier to start with an array instead of an object. It doesn't seem like the object is necessary since the keys and values are identical. Nonetheless, this is a pretty easy task with an array. I converted the obj to an array containing only the values. Then created a new array
filtering out the names we wanted, returned that array sorted alphabetically and in a string format. The ('\n') in the return statement is just delimiting the elements of the array by a new line, since that's the format you showed. But that can be changed to anything, if you don't need the names to be on separate lines.
Just to clarify, as per the documentation, const can be used here:
The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned.
Yes you can manipulate this data, have a look on how const works in javascrips.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
const names = {
John: 'John',
Mike: 'Mike',
David: 'David',
Chris: 'Chris'
};
console.log(names.John);
names.John = 'noJohn';
console.log(names.John);

What's the best way (ES6 allowed) to extract values from an array and convert them to a string?

I'm trying to take an array like so:
location: [
{Id: "000-000", Name: "Foo"},
{Id: "000-001", Name: "Bar"},
..etc
]
What's the most efficient/cleanest way to pull out the Ids and combine them into a single string while also appending in front of each value a static string ("&myId=")?
More succinctly, what's the most efficient way to turn the above array into the following end-result:
&myId=000-000&myId=000-001
As stated in the title, ES6 is acceptable to use if it offers the best method for accomplishing this.
Use reduce, extracting each Id:
const location2 = [{Id: "000-000", Name: "Foo"}, {Id: "000-001", Name: "Bar"}];
console.log(
location2.reduce((a, { Id }) => `${a}&myId=${Id}`, '')
);
While this is pretty clean and only requires iterating over each item once, in terms of efficiency, for loops are still more performant if you have a huge number of items in the array:
const location2 = [{Id: "000-000", Name: "Foo"}, {Id: "000-001", Name: "Bar"}];
let output = '';
for (let i = 0, { length } = location2; i < length; i++) {
output += '&myId=' + location2[i].Id;
}
console.log(output);
In this particular case, it looks like you’re trying to concatenate URL parameters.
You can iterate over the location array and use the appropriate set of APIs for this: URLSearchParams and URL.
In particular, you’re looking for the append method, which allows mapping multiple value to the same key.
const params = new URLSearchParams(),
locationArray = [
{
Id: "000-000",
Name: "Foo"
},
{
Id: "000-001",
Name: "Bar"
}
];
locationArray.forEach(({ Id }) => params.append("myId", Id));
console.log("Result as a string:", String(params));
console.log(`Explicitly calling \`String\` is usually not needed, since ${params} can just be interpolated, concatenated, or coerced to a String like this.`);
console.log("Result inside a URL:", String(Object.assign(new URL(location), { search: params })));
console.log("Result as a URLSearchParams object (look in the browser console (F12) for better formatting):", params);
But in general, using map and join seems efficient enough.
const staticString = "&myId=",
locationArray = [
{
Id: "000-000",
Name: "Foo"
},
{
Id: "000-001",
Name: "Bar"
}
],
result = locationArray.map(({ Id }) => staticString + Id).join("");
// Or:
// result = staticString + locationArray.map(({ Id }) => Id).join(staticString);
console.log(result);
In the alternative, the first staticString may also be changed to "?myId=", since this looks like query parameters.
But it’s important to use the URLSearchParams API if you’re actually using URL parameters, so that the data is correctly encoded.
Try both approaches with one of the Ids having the value "1&myId=2" and you’ll quickly notice the benefit of the URLSearchParams API.
This API also needs to be used to decode everything again.

What is the correct syntax for connecting a variable name to one of its array properties in JavaScript? [duplicate]

I have JavaScript object array with the following structure:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
I want to extract a field from each object, and get an array containing the values, for example field foo would give array [ 1, 3, 5 ].
I can do this with this trivial approach:
function getFields(input, field) {
var output = [];
for (var i=0; i < input.length ; ++i)
output.push(input[i][field]);
return output;
}
var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]
Is there a more elegant or idiomatic way to do this, so that a custom utility function would be unnecessary?
Note about suggested duplicate, it covers how to convert a single object to an array.
Here is a shorter way of achieving it:
let result = objArray.map(a => a.foo);
OR
let result = objArray.map(({ foo }) => foo)
You can also check Array.prototype.map().
Yes, but it relies on an ES5 feature of JavaScript. This means it will not work in IE8 or older.
var result = objArray.map(function(a) {return a.foo;});
On ES6 compatible JS interpreters you can use an arrow function for brevity:
var result = objArray.map(a => a.foo);
Array.prototype.map documentation
Speaking for the JS only solutions, I've found that, inelegant as it may be, a simple indexed for loop is more performant than its alternatives.
Extracting single property from a 100000 element array (via jsPerf)
Traditional for loop 368 Ops/sec
var vals=[];
for(var i=0;i<testArray.length;i++){
vals.push(testArray[i].val);
}
ES6 for..of loop 303 Ops/sec
var vals=[];
for(var item of testArray){
vals.push(item.val);
}
Array.prototype.map 19 Ops/sec
var vals = testArray.map(function(a) {return a.val;});
TL;DR - .map() is slow, but feel free to use it if you feel readability is worth more than performance.
Edit #2: 6/2019 - jsPerf link broken, removed.
Check out Lodash's _.pluck() function or Underscore's _.pluck() function. Both do exactly what you want in a single function call!
var result = _.pluck(objArray, 'foo');
Update: _.pluck() has been removed as of Lodash v4.0.0, in favour of _.map() in combination with something similar to Niet's answer. _.pluck() is still available in Underscore.
Update 2: As Mark points out in the comments, somewhere between Lodash v4 and 4.3, a new function has been added that provides this functionality again. _.property() is a shorthand function that returns a function for getting the value of a property in an object.
Additionally, _.map() now allows a string to be passed in as the second parameter, which is passed into _.property(). As a result, the following two lines are equivalent to the code sample above from pre-Lodash 4.
var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
_.property(), and hence _.map(), also allow you to provide a dot-separated string or array in order to access sub-properties:
var objArray = [
{
someProperty: { aNumber: 5 }
},
{
someProperty: { aNumber: 2 }
},
{
someProperty: { aNumber: 9 }
}
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
Both _.map() calls in the above example will return [5, 2, 9].
If you're a little more into functional programming, take a look at Ramda's R.pluck() function, which would look something like this:
var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray)
Example to collect the different fields from the object array
let inputArray = [
{ id: 1, name: "name1", value: "value1" },
{ id: 2, name: "name2", value: "value2" },
];
let ids = inputArray.map( (item) => item.id);
let names = inputArray.map((item) => item.name);
let values = inputArray.map((item) => item.value);
console.log(ids);
console.log(names);
console.log(values);
Result :
[ 1, 2 ]
[ 'name1', 'name2' ]
[ 'value1', 'value2' ]
It is better to use some sort of libraries like lodash or underscore for cross browser assurance.
In Lodash you can get values of a property in array by following method
_.map(objArray,"foo")
and in Underscore
_.pluck(objArray,"foo")
Both will return
[1, 2, 3]
Using Array.prototype.map:
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
See the above link for a shim for pre-ES5 browsers.
In ES6, you can do:
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
If you want multiple values in ES6+ the following will work
objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];
let result = objArray.map(({ foo, baz }) => ({ foo, baz }))
This works as {foo, baz} on the left is using object destructoring and on the right side of the arrow is equivalent to {foo: foo, baz: baz} due to ES6's enhanced object literals.
While map is a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined.
I'd opt for a reduce solution, which can simply ignore the property or even set you up with a default value.
function getFields(list, field) {
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// check if the item is actually an object and does contain the field
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would work even if one of the items in the provided list is not an object or does not contain the field.
It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.
function getFields(list, field, otherwise) {
// reduce the provided list to an array containing either the requested field or the alternative value
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a map is slightly cheaper than a reduce):
function getFields(list, field, otherwise) {
// map the provided list to an array containing either the requested field or the alternative value
return list.map(function(item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
return typeof item === 'object' && field in item ? item[field] : otherwise;
}, []);
}
jsbin example
And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.
function getFields(list, field, otherwise) {
// determine once whether or not to use the 'otherwise'
var alt = typeof otherwise !== 'undefined';
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
else if (alt) {
carry.push(otherwise);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
jsbin example
As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concat function.
function getFields(list, field, otherwise) {
var alt = typeof otherwise !== 'undefined';
return list.reduce(function(carry, item) {
return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
}, []);
}
jsbin example
The above answer is good for a single property but when select multiple properties from an array use this
var arrayObj=[{Name,'A',Age:20,Email:'a.gmail.com'},{Name,'B',Age:30,Email:'b.gmail.com'},{Name,'C',Age:40,Email:'c.gmail.com'}]
now I select only two fields
var outPutArray=arrayObj.map(( {Name,Email} ) => ({Name,Email}) )
console.log(outPutArray)
If you want to also support array-like objects, use Array.from (ES2015):
Array.from(arrayLike, x => x.foo);
The advantage it has over Array.prototype.map() method is the input can also be a Set:
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
In general, if you want to extrapolate object values which are inside an array (like described in the question) then you could use reduce, map and array destructuring.
ES6
let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);
console.log(b)
The equivalent using for in loop would be:
for (let i in a) {
let temp = [];
for (let j in a[i]) {
temp.push(a[i][j]);
}
array.push(temp);
}
Produced output: ["word", "again", "some", "1", "2", "3"]
If you have nested arrays you can make it work like this:
const objArray = [
{ id: 1, items: { foo:4, bar: 2}},
{ id: 2, items: { foo:3, bar: 2}},
{ id: 3, items: { foo:1, bar: 2}}
];
let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
console.log(result)
Easily extracting multiple properties from array of objects:
let arrayOfObjects = [
{id:1, name:'one', desc:'something'},
{id:2, name:'two', desc:'something else'}
];
//below will extract just the id and name
let result = arrayOfObjects.map(({id, name}) => ({id, name}));
result will be [{id:1, name:'one'},{id:2, name:'two'}]
Add or remove properties as needed in the map function
In ES6, in case you want to dynamically pass the field as a string:
function getFields(array, field) {
return array.map(a => a[field]);
}
let result = getFields(array, 'foo');
It depends on your definition of "better".
The other answers point out the use of map, which is natural (especially for guys used to functional style) and concise. I strongly recommend using it (if you don't bother with the few IE8- IT guys). So if "better" means "more concise", "maintainable", "understandable" then yes, it's way better.
On the other hand, this beauty doesn't come without additional costs. I'm not a big fan of microbench, but I've put up a small test here. The results are predictable, the old ugly way seems to be faster than the map function. So if "better" means "faster", then no, stay with the old school fashion.
Again this is just a microbench and in no way advocating against the use of map, it's just my two cents :).
create an empty array then forEach element from your list, push what you want from that object into your empty array.
let objArray2 = [];
objArray.forEach(arr => objArray2.push(arr.foo));
From an array of objects, extract the value of a property as an array with for loop.
//input
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
//Code
let output=[];
for(let item of objArray){
output.push(item.foo);
}
// Output
[ 1, 3, 5 ]
Above provided answer is good for extracting single property, what if you want to extract more than one property from array of objects.
Here is the solution!!
In case of that we can simply use _.pick(object, [paths])
_.pick(object, [paths])
Lets assume objArray has objects with three properties like below
objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];
Now we want to extract foo and bar property from every object and store them in a separate array.
First we will iterate array elements using map and then we apply Lodash Library Standard _.pick() method on it.
Now we are able to extract 'foo' and 'bar' property.
var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])})
console.log(newArray);
and result would be
[{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]
enjoy!!!
Here is another shape of using map method on array of objects to get back specific property:
const objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
const getProp = prop => obj => obj[prop];
const getFoo = getProp('foo');
const fooes = objArray.map(getFoo);
console.log(fooes);
I would only improve one of the answers if you even don't know the exact property of the object you'r playing with use below:
let result = objArray.map(a => a[Object.getOwnPropertyNames(a)]);
Function map is a good choice when dealing with object arrays. Although there have been a number of good answers posted already, the example of using map with combination with filter might be helpful.
In case you want to exclude the properties which values are undefined or exclude just a specific property, you could do the following:
var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
https://jsfiddle.net/ohea7mgk/
Destructure and get specific attributes from array of object:
const customerList = dealerUserData?.partyDetails.map(
({ partyId, custAccountId }) => ({
partyId,
custAccountId,
customerId: dealerUserData?._id,
userId: dealerUserData?.authUserID,
}),
);

Categories

Resources