Related
I had an array of objects with duplicated ids. Each object associated with the id has a property A and property B. For each loop, first index array's property A has value, but property B is null. In second index array, property A null, property B has value. The first two index will merge according to the same id. The output doesn't produce values for both property A and property B, but either one still null.
Array
{
id: 123,
A: “value A1”
B: null
},
{
id: 123,
A: null,
B: “value b”
},
{
id: 123,
A: “value A2”
B: null
},
{
id: 456,
A: "a2 value",
B: "b2 value"
}
Code
var output = _.groupBy(arr, function(o){
return o.id;
})
Output
{
id: 123,
A: [“value A1”,“value A2”]
B: null
},
{
id: 456,
A: ["a2 value"],
B: "b2 value"
}
Expected
{
id: 123,
A: [“value A1”,“value A2”]
B: “value b”
},
{
id: 456,
A: ["a2 value"],
B: "b2 value"
}
You can do it without underscore and loadash:
var arr = [
{
id: 123,
A: "value A1",
B: null
},
{
id: 123,
A: null,
B: "value b"
},
{
id: 123,
A: "value A2",
B: null
},
{
id: 456,
A: "a2 value",
B: "b2 value"
}
];
var arr2 = arr.reduce((m, o) => {
var sameObj = m.find(it => it.id === o.id);
if (sameObj) {
o.A && (sameObj.A.push(o.A));
sameObj.B = sameObj.B || o.B;
} else {
o.A = o.A ? [o.A] : [];
m.push(o);
}
return m;
}, []);
console.log(arr2);
Following is without underscore or lodash. Just with vanilla JavaScript.
var data = [{
id: 123,
A: "some value",
B: null
},
{
id: 123,
A: null,
B: "b value"
},
{
id: 456,
A: "a2 value",
B: "b2 value"
}
];
var outputObject = {},
outputArray = [];
data.forEach(function(obj) {
if (!outputObject[obj.id]) {
outputObject[obj.id] = obj;
} else {
if (obj.B !== null) {
outputObject[obj.id].B = obj.B;
}
if (obj.A !== null) {
outputObject[obj.id].A = obj.A;
}
}
});
//Convert to an array
Object.keys(outputObject).forEach(function(key) {
outputArray.push(outputObject[key]);
});
console.log(outputArray);
I have an array of objects as follows:
c = [{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}]
I want to kind-of sort them by the objects having values first then objects with null.
What I tried is:
c.sort(function(b) { return b.a ? -1 : 1 })
OUTPUT
[{a: 2}, {a: 50}, {a: 1}, {a: 12}, {a: null}, {a: null}]
EXPECTED OUTPUT
[{a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}, {a: null}]
How can I achieve this?
This will put nulls and other falsy values to the end of the list:
c = [{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}];
c.sort((x, y) => !!y.a - !!x.a);
console.log(c);
However, since you don't really sort anything, you can just split the list into two parts an rejoin them:
c = [{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}];
r = [
...c.filter(x => x.a !== null),
...c.filter(x => x.a === null)
]
console.log(r)
This also doesn't rely on the sort function being stable.
You could test the value. If null, then take the delta of the comparison.
var c = [{ a: null }, { a: 12 }, { a: 1 }, { a: 50 }, { a: 2 }, { a: null }];
c.sort(function (a, b) {
return (a.a === null) - (b.a === null);
});
console.log(c);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For a stable sort, you could use sorting with map and use the indices as second sort option.
// the array to be sorted
var list = [{ a: null }, { a: 12 }, { a: 1 }, { a: 50 }, { a: 2 }, { a: null }];
// temporary array holds objects with position and sort-value
var mapped = list.map(function(el, i) {
return { index: i, value: el.a === null};
});
// sorting the mapped array containing the reduced values
mapped.sort(function(a, b) {
return a.value - b.value || a.index - b.index;
});
// container for the resulting order
var result = mapped.map(function(el){
return list[el.index];
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const c = [{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}];
let result = [...c.filter(_=>_["a"]!==null),...c.filter(_=>_["a"]===null)];
console.log(result);
Try with return !a.a - !b.a .valid object goes first
var c = [{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}];
c.sort(function (a, b) {
return !a.a - !b.a
});
console.log(c);
That way ?
c=[{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}];
c.sort(function(a,b){ return a.a===null ? 1:-1 })
console.log(c);
Edited
var c = [{
a: null
}, {
a: 12
}, {
a: 1
}, {
a: 50
}, {
a: 2
}, {
a: null
}];
c.sort(function(a, b) {
return (a.a !== null) ? 0 : 1;
});
console.log(c);
Returning 0 in the sort function will keep the order as it is.
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
It is not perfect but still works,cheers!
var c = [{a: null}, {a: 12}, {a: 1}, {a: 50}, {a: 2}, {a: null}]
c.sort(function(object1,object2){
if(object1.a === null && object2.a !== null){return 1}
if([object1.a,object2.a].every((a)=>a === null) ||
[object1.a,object2.a].every((a)=>a !== null)
){return 0}
if(object1.a !== null && object2.a === null){return -1}
})
console.log(c);
Here's a case of sorting a Date with null value at the end if exists:
userSortList = users.sort((a, b) => {
if (b.lastConnectionDate === null || a.lastConnectionDate) {
return -1
} else {
return (
new Date(b.lastConnectionDate) - new Date(a.lastConnectionDate)
)
}
})
Consider the following object :
{ a1: "Foo", b1: "Boo" , c1: "Coo", a2: "Doo" , b2: "Goo", c2: "Soo", ....... c50: "Zoo" }
I want to divide this in an array of arrays that will look like this :
[["Foo","Boo","Coo"], ["Doo","Goo","Soo"]......[ .., ..,"Zoo"]]
What is the best practice for doing that ?
You can first use reduce() on Object.keys() to return one object and then map() keys of that object to return values which will be array of values.
var obj = { a1: "Foo", b1: "Boo" , c1: "Coo", a2: "Doo" , b2:"Goo", c2:"Soo", c50:"Zoo"}
var o = Object.keys(obj).reduce(function(r, e) {
var newKey = e.match(/\d+/)[0]
r[newKey] = (r[newKey] || []).concat(obj[e])
return r
}, {})
var result = Object.keys(o).map(e => o[e])
console.log(result)
With ES-7 you could use Object.values() but it still has bad browser support, but the solution looks like this.
var obj = { a1: "Foo", b1: "Boo" , c1: "Coo", a2: "Doo" , b2:"Goo", c2:"Soo", c50:"Zoo"}
var result = Object.values(Object.keys(obj).reduce(function(r, e) {
var newKey = e.match(/\d+/)[0]
return r[newKey] = (r[newKey] || []).concat(obj[e]), r
}, {}))
console.log(result)
You could get the keys with Object.keys, iterate with Array#forEach over them, get the numerical part with String#match, decrement it for the index and push the value.
var object = { a1: "Foo", b1: "Boo" , c1: "Coo", a2: "Doo" , b2:"Goo", c2:"Soo", c50:"Zoo"},
result = [];
Object.keys(object).forEach(function (key) {
var i = key.match(/\d+$/) - 1;
result[i] = result[i] || [];
result[i].push(object[key]);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try something like this:
var obj = { a1: "Foo", b1: "Boo" , c1: "Coo", a2: "Doo" , b2:"Goo", c2:"Soo" };
var indexObj = { a: 0, b: 1, c: 2 };
var result = [];
for(var prop in obj) {
var index = indexObj[prop[0]];
var array = prop.match(/\d+$/) - 1;
if(!result[array]) {
result[array] = [];
}
result[array][index] = obj[prop];
}
console.log(JSON.stringify(result));
//[["Foo","Boo","Coo"],["Doo","Goo","Soo"]]
May be you can do as follows;
var obj = { a1: "Foo", b1: "Boo" , c1: "Coo", a2: "Doo" , b2: "Goo", c2: "Soo", a3: "Hello", b3: "to", c3: "you", c50: "Zoo" },
res = Object.keys(obj)
.reduce(function(r,c){
var t = /\d+/.exec(c)[0];
r.t = r.t !== t ? (r.push([obj[c]]), t)
: (r[r.length-1].push(obj[c]), t);
return r;
},[]);
console.log(res);
I'd like to convert an object to an array of smaller, single-property objects in Javascript.
For example:
x={
a: {a1:"a1", a2:"a2"},
b: {b1:"b1", b2:"b2"},
c: {c1:"c1", c2:"c2"}
}
converted to
y=[
{a: {a1:"a1", a2:"a2"}},
{b: {b1:"b1", b2:"b2"}},
{c: {c1:"c1", c2:"c2"}}
]
I know this is possible with loops, but I was wondering if there is a more elegant way. I'm using underscore/lo-dash if they help.
You could use Array#map for it.
var x = { a: { a1: "a1", a2: "a2" }, b: { b1: "b1", b2: "b2" }, c: { c1: "c1", c2: "c2" } },
y = Object.keys(x).map(function (k) {
var o = {};
o[k] = x[k];
return o;
});
console.log(y);
ES6 #Nenad Vracar
var x = { a: { a1: "a1", a2: "a2" }, b: { b1: "b1", b2: "b2" }, c: { c1: "c1", c2: "c2" } },
y = Object.keys(x).map(k => ({[k]: x[k]}));
console.log(y);
As Barmar pointed out, it does make more sense to use map (I left my reduce versions for posterity):
let arr = Object.keys(x).map(key => { [key]: x[key] });
You can use reduce to turn it into an array:
Object.keys(x).reduce(function(acc, key) {
var tmp = {};
tmp[key] = x[key];
acc.push(tmp);
return acc;
}, []);
And if ES6 is available, you can make it a bit more concise:
Object.keys(x).reduce((acc, key) => {
acc.push({ [key]: x[key] });
return acc;
}, []);
Given input:
[{ a: 1 }, { b: 2 }, { c: 3 }]
How to return:
{ a: 1, b: 2, c: 3 }
For arrays it's not a problem with lodash but here we have array of objects.
Use Object.assign:
let merged = Object.assign(...arr); // ES6 (2015) syntax
var merged = Object.assign.apply(Object, arr); // ES5 syntax
Note that Object.assign is not yet implemented in many environment and you might need to polyfill it (either with core-js, another polyfill or using the polyfill on MDN).
You mentioned lodash, so it's worth pointing out it comes with a _.assign function for this purpose that does the same thing:
var merged = _.assign.apply(_, [{ a: 1 }, { b: 2 }, { c: 3 }]);
But I really recommend the new standard library way.
With lodash, you can use merge():
var arr = [ { a: 1 }, { b: 2 }, { c: 3 } ];
_.merge.apply(null, [{}].concat(arr));
// → { a: 1, b: 2, c: 3 }
If you're doing this in several places, you can make merge() a little more elegant by using partial() and spread():
var merge = _.spread(_.partial(_.merge, {}));
merge(arr);
// → { a: 1, b: 2, c: 3 }
Here is a version not using ES6 methods...
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var obj = {};
for(var i = 0; i < arr.length; i++) {
var o = arr[i];
for(var key in o) {
if(typeof o[key] != 'function'){
obj[key] = o[key];
}
}
}
console.log(obj);
fiddle: http://jsfiddle.net/yaw3wbb8/
You can use underscore.extend function like that:
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = _.extend.apply(null, a);
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
And to prevent modifying original array you should use
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = _.extend.apply(null, [{}].concat(a));
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1 }, { b: 2 }, { c: 3 } ]
Here can test it
Adding to the accepted answer, a running code snippet with ES6.
let input = [{ a: 1 }, { b: 2 }, { c: 3 }]
//Get input object list with spread operator
console.log(...input)
//Get all elements in one object
console.log(Object.assign(...input))
I've got a neat little solution not requiring a polyfill.
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var object = {};
arr.map(function(obj){
var prop = Object.getOwnPropertyNames(obj);
object[prop] = obj[prop];
});
Hope that helps :)
Here is a nice usage of Object.assign with the array.prototype.reduce function:
let merged = arrOfObjs.reduce((accum, val) => {
Object.assign(accum, val);
return accum;
}, {})
This approach does not mutate the input array of objects, which could help you avoid difficult to troubleshoot problems.
With more modern spread operator
arrOfObj.reduce( (acc, curr) => ({ ...acc, ...cur }) );
You can easily flat your object to array.
function flatten(elements) {
return elements.reduce((result, current) => {
return result.concat(Array.isArray(current) ? flatten(current) : current);
}, []);
};
6 years after this question was asked.
Object.assign is the answer (above) I like the most.
but is this also legal ?
let res = {};
[{ a: 1 }, { b: 2 }, { c: 3 }].forEach(val => {
let key = Object.keys(val);
console.log(key[0]);
res[key] = val[key];
})
const data = [
[{ a: "a" }, { b: "b" }, { c: "c" }],
[{ d: "d" }, { e: "e" }, { f: "f" }],
[{ g: "g" }, { h: "h" }, { i: "i" }],
];
function convertToObject(array){
const response = {};
for (let i = 0; i < array.length; i++) {
const innerArray = array[i];
for (let i = 0; i < innerArray.length; i++) {
const object = innerArray[i];
const keys = Object.keys(object);
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
response[key] = object[key];
}
}
}
return response;
}
console.log(convertToObject(data));
function carParts(manufacturer, model, ...parts) {
return { manufacturer, model, ...Object.assign(...parts) };
}
console.log(
carParts(
"Honda",
"2008",
{ color: "Halogen Lights" },
{ Gears: "Automatic Gears" },
{ LED: "Android LED" },
{ LED: "Android LED1" }
)
);
This is how i have done.