Related
I have a given object to iterate and create another object in the below format (newObj)
Given object:
let obj = [
{a: 1, b: 232},
{a: 1, b: 2},
{a: 1, b: 256},
{a: 2, b: 3},
{a: 2, b: 3343},
{a: 3, b: 4}
];
Expected object:
newObj = {
1: [{a: 1, b: 232}, {a: 1, b: 2}, {a: 1, b: 256}],
2: [{a: 2, b: 3}],
3: [{a: 3, b: 4}]
}
Code:
let newObj = {};
obj.forEach(element => {
if (newObj[element.a]) {
let key = element.a;
newObj[key] = newObj[key].push(element);
}
newObj[element.a] = [element];
});
console.log(newObj);
We create a result object, loop through every object in obj array, if we don't have the object a key in the result object, we add him (a: []), and after that we push the entire object to result[a] array
let obj = [
{a: 1, b: 232},
{a: 1, b: 2},
{a: 1, b: 256},
{a: 2, b: 3},
{a: 2, b: 3343},
{a: 3, b: 4}
];
let result = {};
for(const {a, b} of obj) {
if(!result[a]) result[a] = []
result[a].push({a, b})
}
console.log(result)
To address your updated code snippet, you need to only create a new array for a given key if they key does not exist.
let obj = [
{ a: 1, b: 232 },
{ a: 1, b: 2 },
{ a: 1, b: 256 },
{ a: 2, b: 3 },
{ a: 2, b: 3343 },
{ a: 3, b: 4 }
];
let newObj = {};
obj.forEach(element => {
let key = element.a;
if (!newObj[key]) {
newObj[key] = []; // Only initialize if undefined/null
}
newObj[key].push(element); // Always push
});
console.log(newObj);
.as-console-wrapper { top: 0; max-height: 100% !important; }
A more modern approach would be to simply bin them by the a key by reducing and spreading.
const obj = [
{ a: 1, b: 232 },
{ a: 1, b: 2 },
{ a: 1, b: 256 },
{ a: 2, b: 3 },
{ a: 2, b: 3343 },
{ a: 3, b: 4 }
];
const newObj = obj.reduce((acc, o) => ({
...acc,
[o.a]: [...(acc[o.a] ?? []), o]
}), {});
console.log(newObj);
.as-console-wrapper { top: 0; max-height: 100% !important; }
you can use reduce for that
let obj = [
{a: 1, b: 232},
{a: 1, b: 2},
{a: 1, b: 256},
{a: 2, b: 3},
{a: 2, b: 3343},
{a: 3, b: 4}
];
const newObj = obj.reduce((res, {a, b}) => {
return {
...res,
[a] : [...(res[a] || []), {a, b}]
}
}, {})
console.log(newObj)
This question already has answers here:
Find object by id in an array of JavaScript objects
(36 answers)
Closed 4 years ago.
Let's say I have an array of four objects:
var jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
];
Is there a way that I can get the third object ({a: 5, b: 6}) by the value of the property b for example without a for...in loop?
Filter array of objects, which property matches value, returns array:
var result = jsObjects.filter(obj => {
return obj.b === 6
})
See the MDN Docs on Array.prototype.filter()
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
]
let result = jsObjects.filter(obj => {
return obj.b === 6
})
console.log(result)
Find the value of the first element/object in the array, otherwise undefined is returned.
var result = jsObjects.find(obj => {
return obj.b === 6
})
See the MDN Docs on Array.prototype.find()
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
]
let result = jsObjects.find(obj => {
return obj.b === 6
})
console.log(result)
jsObjects.find(x => x.b === 6)
From MDN:
The find() method returns a value in the array, if an element in the array satisfies the provided testing function. Otherwise undefined is returned.
Side note: methods like find() and arrow functions are not supported by older browsers (like IE), so if you want to support these browsers, you should transpile your code using Babel.
I don't know why you are against a for loop (presumably you meant a for loop, not specifically for..in), they are fast and easy to read. Anyhow, here's some options.
For loop:
function getByValue(arr, value) {
for (var i=0, iLen=arr.length; i<iLen; i++) {
if (arr[i].b == value) return arr[i];
}
}
.filter
function getByValue2(arr, value) {
var result = arr.filter(function(o){return o.b == value;} );
return result? result[0] : null; // or undefined
}
.forEach
function getByValue3(arr, value) {
var result = [];
arr.forEach(function(o){if (o.b == value) result.push(o);} );
return result? result[0] : null; // or undefined
}
If, on the other hand you really did mean for..in and want to find an object with any property with a value of 6, then you must use for..in unless you pass the names to check.
Example
function getByValue4(arr, value) {
var o;
for (var i=0, iLen=arr.length; i<iLen; i++) {
o = arr[i];
for (var p in o) {
if (o.hasOwnProperty(p) && o[p] == value) {
return o;
}
}
}
}
Ways to achieve the requirement :
Using Array.find() method :
const jsObject = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
];
const filteredResult = jsObject.find((e) => e.b == 6);
console.log(filteredResult);
Using Array.filter() method :
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
];
const filterObj = jsObjects.filter((e) => e.b == 6);
console.log(filterObj[0]);
Using for...in loop :
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
];
for (const i in jsObjects) {
if (jsObjects[i].b == 6) {
console.log(jsObjects[i]);
}
}
OK, there are few ways to do that, but let's start with the simplest one and latest approach to do this, this function is called find().
Just be careful when you using find to as even IE11 dosn't support it, so it needs to be transpiled...
so you have this object as you said:
var jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
];
and you can write a function and get it like this:
function filterValue(obj, key, value) {
return obj.find(function(v){ return v[key] === value});
}
and use the function like this:
filterValue(jsObjects, "b", 6); //{a: 5, b: 6}
Also in ES6 for even shortened version:
const filterValue = (obj, key, value)=> obj.find(v => v[key] === value);
This method only return the first value which match..., for better result and browser support, you can use filter:
const filterValue = (obj, key, value)=> obj.filter(v => v[key] === value);
and we will return [{a: 5, b: 6}]...
This method will return an array instead...
You simpley use for loop as well, create a function like this:
function filteredArray(arr, key, value) {
const newArray = [];
for(i=0, l=arr.length; i<l; i++) {
if(arr[i][key] === value) {
newArray.push(arr[i]);
}
}
return newArray;
}
and call it like this:
filteredArray(jsObjects, "b", 6); //[{a: 5, b: 6}]
See this documentation Array.prototype.find()
Example:
var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
function findCherries(fruit) {
return fruit.name === 'cherries';
}
console.log(inventory.find(findCherries));
// { name: 'cherries', quantity: 5 }
Using underscore.js:
var foundObject = _.findWhere(jsObjects, {b: 6});
It looks like in the ECMAScript 6 proposal there are the Array methods find() and findIndex(). MDN also offers polyfills which you can include to get the functionality of these across all browsers.
find():
function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) return false;
}
return (element > 1);
}
console.log( [4, 6, 8, 12].find(isPrime) ); // undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5
findIndex():
function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) return false;
}
return (element > 1);
}
console.log( [4, 6, 8, 12].findIndex(isPrime) ); // -1, not found
console.log( [4, 6, 7, 12].findIndex(isPrime) ); // 2
If I understand correctly, you want to find the object in the array whose b property is 6?
var found;
jsObjects.some(function (obj) {
if (obj.b === 6) {
found = obj;
return true;
}
});
Or if you were using underscore:
var found = _.select(jsObjects, function (obj) {
return obj.b === 6;
});
If you are looking for a single result, rather than an array, may I suggest reduce?
Here is a solution in plain 'ole javascript that returns a matching object if one exists, or null if not.
var result = arr.reduce(function(prev, curr) { return (curr.b === 6) ? curr : prev; }, null);
You can use it with the arrow function as well like as below :
var demoArray = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
var result = demoArray.filter( obj => obj.name === 'apples')[0];
console.log(result);
// {name: 'apples', quantity: 2}
How about using _.find(collection, [predicate=_.identity], [fromIndex=0]) of lo-dash to get object from array of objects by object property value. You could do something like this:
var o = _.find(jsObjects, {'b': 6});
Arguments:
collection (Array|Object): The collection to inspect.
[predicate=_.identity] (Function): The function invoked per iteration.
[fromIndex=0] (number): The index to search from.
Returns
(*): Returns the matched element (in your case, {a: 5, b: 6}), else undefined.
In terms of performance, _.find() is faster as it only pulls the first object with property {'b': 6}, on the other hand, if suppose your array contains multiple objects with matching set of properties (key:value), then you should consider using _.filter() method. So in your case, as your array has a single object with this property, I would use _.find().
Made the best/fastest part of this answer more re-usable & clear:
function getElByPropVal(myArray, prop, val){
for (var i = 0, length = myArray.length; i < length; i++) {
if (myArray[i][prop] == val){
return myArray[i];
}
}
}
var result = jsObjects.filter(x=> x.b === 6);
will be better, using return in filter sometimes you can't get result (I dunno why)
To get first object from array of objects by a specific property value:
function getObjectFromObjectsArrayByPropertyValue(objectsArray, propertyName, propertyValue) {
return objectsArray.find(function (objectsArrayElement) {
return objectsArrayElement[propertyName] == propertyValue;
});
}
function findObject () {
var arrayOfObjectsString = document.getElementById("arrayOfObjects").value,
arrayOfObjects,
propertyName = document.getElementById("propertyName").value,
propertyValue = document.getElementById("propertyValue").value,
preview = document.getElementById("preview"),
searchingObject;
arrayOfObjects = JSON.parse(arrayOfObjectsString);
console.debug(arrayOfObjects);
if(arrayOfObjects && propertyName && propertyValue) {
searchingObject = getObjectFromObjectsArrayByPropertyValue(arrayOfObjects, propertyName, propertyValue);
if(searchingObject) {
preview.innerHTML = JSON.stringify(searchingObject, false, 2);
} else {
preview.innerHTML = "there is no object with property " + propertyName + " = " + propertyValue + " in your array of objects";
}
}
}
pre {
padding: 5px;
border-radius: 4px;
background: #f3f2f2;
}
textarea, button {
width: 100%
}
<fieldset>
<legend>Input Data:</legend>
<label>Put here your array of objects</label>
<textarea rows="7" id="arrayOfObjects">
[
{"a": 1, "b": 2},
{"a": 3, "b": 4},
{"a": 5, "b": 6},
{"a": 7, "b": 8, "c": 157}
]
</textarea>
<hr>
<label>property name: </label> <input type="text" id="propertyName" value="b"/>
<label>property value: </label> <input type="text" id="propertyValue" value=6 />
</fieldset>
<hr>
<button onclick="findObject()">find object in array!</button>
<hr>
<fieldset>
<legend>Searching Result:</legend>
<pre id="preview">click find</pre>
</fieldset>
Using find with bind to pass specific key values to a callback function.
function byValue(o) {
return o.a === this.a && o.b === this.b;
};
var result = jsObjects.find(byValue.bind({ a: 5, b: 6 }));
var jsObjects = [{a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6}, {a: 7, b: 8}];
to access the third object, use: jsObjects[2];
to access the third object b value, use: jsObjects[2].b;
Say I have the following array:
let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
I would like to compute the cumulative sum of each key, but I would also like the output to be an array of the same length with the cumulative values at each step. The final result should be:
[{a: 1, b: 2}, {a: 3, b: 6}, {a: 11, b: 5}]
My issue is that I am not able to obtain the array as desired. I only get the final object with this:
let result = arr.reduce((accumulator, element) => {
if(accumulator.length === 0) {
accumulator = element
} else {
for(let i in element){
accumulator[i] = accumulator[i] + element[i]
}
}
return accumulator
}, [])
console.log(result); // {a: 11, b: 5}
What you're after sounds like the scan() higher-order function (borrowing the idea from ramda.js), which allows you to return an accumulated result for each element within your array. The scan method is similar to how the .reduce() method behaves, except that it returns the accumulator for each element. You can build the scan() function yourself like so:
let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}];
const scan = ([x, ...xs], fn) => xs.reduce((acc, elem) => {
return [...acc, fn(acc.at(-1), elem)];
}, xs.length ? [x] : []);
const res = scan(arr, (x, y) => ({a: x.a+y.a, b: x.b+y.b}));
console.log(res);
You might consider further improvements such as providing an initial value to the scan method (similar to how reduce accepts one). Also, if you need better browser support the .at() method currently has limited browser support, so you may instead consider creating your own at() function:
const at = (arr, idx) => idx >= 0 ? arr[idx] : arr[arr.length + idx];
You can easily achieve the result using reduce as
let arr = [
{ a: 1, b: 2 },
{ a: 2, b: 4 },
{ a: 8, b: -1 },
];
const result = arr.reduce((acc, curr, i) => {
if (i === 0) acc.push(curr);
else {
const last = acc[i - 1];
const newObj = {};
Object.keys(curr).forEach((k) => (newObj[k] = curr[k] + last[k]));
acc.push(newObj);
}
return acc;
}, []);
console.log(result);
Something like this:
const arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
const result = arr.reduce((accumulator, element, index) => {
if(accumulator.length === 0) {
accumulator.push(element)
} else {
const sum = {};
for(let i in element) {
sum[i] = element[i] + (accumulator[index - 1][i] || 0)
}
accumulator.push(sum)
}
return accumulator
}, [])
console.log(result);
Another option is keep sum result using a Map, it helps if keys in elements of the array are not always same.
const arr = [{a: 1, b: 2}, {a: 2}, {a: 8, b: -1}];
const map = new Map();
const result = arr.map((element) => {
const sum = {};
for (let i in element) {
sum[i]= element[i] + (map.get(i) || 0);
map.set(i, sum[i]);
}
return sum;
});
console.log(result);
Here is a bit more concise reduce, probably not as readable as a consequence...
array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
let array = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
let culm = array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
console.log(culm)
Given:
const xs =
[ {a: 1, b: 2}
, {a: 2, b: 4}
, {a: 8, b: -1}];
Define a function sum such as:
const sum = ([head, ...tail]) =>
tail.reduce((x, y) =>
({a: (x.a+y.a), b: (x.b+y.b)}), head);
sum(xs);
//=> {a: 11, b: 5}
Then apply that function in a map on larger slices of xs:
xs.map((_, i, arr) => sum(arr.slice(0, i+1)));
//=> [ {a: 1, b: 2}
//=> , {a: 3, b: 6}
//=> , {a: 11, b: 5}]
How to check if all objects in an array contains same keys and values
const arrOfObjects = [{a: 1, b: 2}, {a: 1, b: 2}, {a:1, b: 2 }] // true
const arrOfObjects = [{a: 1, b: 2}, {a: 1, b: 2}, {a:2, b: 1 }] //false
const arrOfObjects = [{a: 1, b: 2}, {a: 1, b: 2, c: 3}, {a:2, b: 1 }] //false
This is my trial that looks so ugly and bad and not working, i would be thankful if someone put an efficient code for that problem!
function test(arr){
const firstItem = arr[0];
const firstItemKeys = Object.keys(firstItem);
for(let i = 0; i < firstItemKeys.length; i++) {
for(let j = 0; j < arr.length; j++) {
for(let x in arr[j]) {
if(arr[j][x] !== firstItem[firstItemKeys[i]]) return false
}
}
}
return true
}
Here is the code:
const arrOfObjects = [
{ a: 1, b: 2 },
{ a: 1, b: 2 },
{ b: 2, a: 1 },
]
function areEquals(a, b) {
var keys1 = Object.keys(a)
var keys2 = Object.keys(b)
if(keys1.length !== keys2.length) {
return false ;
}
for(key in a) {
if(a[key] !== b[key]) return false;
}
return true ;
}
function checkArray(arr) {
for (var i = 1; i < arr.length; i++) {
if (!areEquals(arr[0], arr[i])) return false
}
return true
}
console.log(checkArray(arrOfObjects))
If you can use lodash, then there is method _.isEqual
const _ = require('lodash')
const arrOfObjects = [{a: 1, b: 2}, {a: 1, b: 2}, {a:1, b: 2 }]
let isEqual = true
arrOfObjects.forEach(obj => {
if (!_.isEqual(arrOfObjects[0], obj)) {
isEqual = false
}
})
return isEqual
PS: This could be written in one line with reduce, but it will not be readable for anyone new to programming or javascript.
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]