Mass subset record objects in javascript - javascript

I am looking for the most efficient way to extract a subset of elements from a javascript object. Suppose I have many records in the form of javascript objects
var records = [
{ foo: 123, bar: 456, baz: 789, boo: [1,2,3] },
{ foo: 345, bar: 754, baz: 853, boo: [4,3,2] },
{ foo: 321, bar: 234, baz: 443, boo: [5,2,1] }
]
And for each of these objects I want to extract the same keys, which are stored in an array, e.g.
var keys = ["bar", "boo"]
The native way to do so would be along the lines of
out = [];
for(var i = 0; i < records.length; i++){
out[i] = {};
for(var j = 0; j < keys.length; j++){
var field = keys[j];
out[i][field] = records[i][field]
}
}
console.log(out)
Is there a faster / more elegant way?

Not sure about speed, but a more concise notation could be (assuming all keys are present in each record):
var out = records.map(function(v) {
return keys.reduce(function(o, k) {
return (o[k] = v[k], o);
}, {});
});

I can't think of a better Big O notation for your problem, but looking at the current data, a good algoritm may not be your primary focus?
In ECMA-script 5 the base Object contains a keys method:
a = {a: '1', b: '2', c: '3'}
Object.keys(a)
// [ 'a', 'b', 'c' ]

Related

extracting object from array of array using es6

how can i return an array of objects taking from array of again one more level array. I am using push.
is there any better way to achieve this
let a = [{b: [{c: "k"}]}]
let o = []
a.forEach(so => {so.b.forEach(obc => o.push(obc))})
console.log(o)
I'd use flatMap() instead:
const a = [{
b: [{
foo: 'foo'
}]
},
{
b: [{
c: "k"
},
{
bar: 'bar'
}
]
}
];
const o = a.flatMap(({ b }) => b);
console.log(o);
(but this is a relatively new method, so if you want to use it and still support older environments, be sure to include a polyfill)
Lacking that, you can also improve your existing code by using concat() with the inner array instead of iterating over each inner item:
const a = [{
b: [{
foo: 'foo'
}]
},
{
b: [{
c: "k"
},
{
bar: 'bar'
}
]
}
];
let o = [];
a.forEach(({ b }) => {
o = o.concat(b);
});
console.log(o);
Try
let a = [{b: [{c: "k"}]}]
let o =a[0].b
console.log(o)

Combining arrays for use cases

Node.js app, writing validation tests. Given the following:
var obj = { foo: null, bar: null, baz: null},
values = [ 0, 1];
I need to create n number of objects to account for every property being assigned every combination of possible values, to represent every possible use case. So for this example, the output should be 2^3=8 objects, e.g.
[
{ foo: 0, bar: 0, baz: 0},
{ foo: 0, bar: 1, baz: 0},
{ foo: 0, bar: 1, baz: 1},
{ foo: 0, bar: 0, baz: 1},
{ foo: 1, bar: 0, baz: 0},
{ foo: 1, bar: 1, baz: 0},
{ foo: 1, bar: 1, baz: 1},
{ foo: 1, bar: 0, baz: 1},
]
Underscore or lodash or other libraries are acceptable solutions. Ideally, I would like something like so:
var mapUseCases = function(current, remaining) {
// using Underscore, for example, pull the current case out of the
// possible cases, perform logic, then continue iterating through
// remaining cases
var result = current.map(function(item) {
// perform some kind of logic, idk
return magic(item);
});
return mapUseCases(result, _.without(remaining, current));
}
var myValidationHeadache = mapUseCases(currentThing, somethingElse);
Pardon my pseudocode, I think I broke my brain. ¯\_(ツ)_/¯
Solution for any object length and any values.
Please note, undefined values do not show up.
function buildObjects(o) {
var keys = Object.keys(o),
result = [];
function x(p, tupel) {
o[keys[p]].forEach(function (a) {
if (p + 1 < keys.length) {
x(p + 1, tupel.concat(a));
} else {
result.push(tupel.concat(a).reduce(function (r, b, i) {
r[keys[i]] = b;
return r;
}, {}));
}
});
}
x(0, []);
return result;
}
document.write('<pre>' + JSON.stringify(buildObjects({
foo: [0, 1, 2],
bar: [true, false],
baz: [true, false, 0, 1, 42]
}), 0, 4) + '</pre>');
One way is to count from "000" to "999" in a values.length-based system:
keys = ['foo','bar','baz']
values = ['A', 'B']
width = keys.length
base = values.length
out = []
for(var i = 0; i < Math.pow(base, width); i++) {
var d = [], j = i;
while(d.length < width) {
d.unshift(j % base)
j = Math.floor(j / base)
}
var p = {};
for(var k = 0; k < width; k++)
p[keys[k]] = values[d[k]]
out.push(p)
}
document.write('<pre>'+JSON.stringify(out,0,3))
Update for products:
'use strict';
let
keys = ['foo', 'bar', 'baz'],
values = [
['A', 'B'],
['a', 'b', 'c'],
[0, 1]
];
let zip = (h, t) =>
h.reduce((res, x) =>
res.concat(t.map(y => [x].concat(y)))
, []);
let product = arrays => arrays.length
? zip(arrays[0], product(arrays.slice(1)))
: [[]];
let combine = (keys, values) =>
keys.reduce((res, k, i) =>
(res[k] = values[i], res)
, {});
let z = product(values).map(v => combine(keys, v));
z.map(x => document.write('<pre>'+JSON.stringify(x)+'</pre>'))
This is a non-recursive version of what you want:
function createRange(keys, values) {
if (typeof values[0] !== typeof [])
values = keys.map(k => values);
var pointer = {};
var repeats = 1;
keys.forEach((k, i) => {
var vLen = values[i].length;
repeats *= vLen;
pointer[k] = {
get value() {
return values[i][pointer[k].current]
},
current: 0,
period: Math.pow(vLen, i),
inc: function() {
var ptr = pointer[k];
ptr.current++;
if (ptr.current < vLen) return;
ptr.current = 0;
if (i + 1 === keys.length) return;
var nk = keys[i + 1];
pointer[nk].inc()
}
};
});
var result = [];
for (var i = 0; i < repeats; i++) {
var o = {};
result.push(o);
keys.forEach(k => o[k] = pointer[k].value)
pointer[keys[0]].inc();
}
return result;
}
var objKeys = ['u', 'v', 'w', 'x', 'y', 'z'];
var objValues = [
['1', '2', '3'],
['a', 'b', 'c'],
['foo', 'bar', 'baz'],
[1, 3, 2],
['test', 'try', 'catch'],
['Hello', 'World'],
];
var range = createRange(objKeys, objValues);
range.map(v => document.write(JSON.stringify(v).big()))

Flatten array with objects into 1 object

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.

Can I get the benefits of both, objects and arrays in JavaScript?

I have parts of data tagged with IDs distributed in different collections.
I need to access this data directly with the ID, but I also need to loop over the data really fast.
var listOfPartA = {
34523: { foo: 7, bar: 123},
6435: { foo: 2, bar: 163},
3123: { foo: 3, bar: 223},
...
};
var listOfPartB = {
34523: { baz: 1},
6435: { baz: 4},
3123: { baz: 6},
...
};
If I need to get specific dataparts for an Id it's fast, but If I try to loop all the dataparts it's slow.
var listOfPartA = [
{ id: 34523, foo: 7, bar: 123},
{ id: 6435, foo: 2, bar: 163},
{ id: 3123, foo: 3, bar: 223},
...
];
var listOfPartB = [
{ id: 34523, baz: 1},
{ id: 6435, baz: 4},
{ id: 3123, baz: 6},
...
];
If I want to get specific dataparts for an ID of these objects it is slow, because I have to search for them manually, but If I loop over all dataparts it's fast.
Can't I have fast direct access and fast iteration?
Pre-index your array:
var listOfPartA = [
{id:34523, foo:7 bar:123},
...
];
var lookupPartA = {};
listOfPartA.forEach(function(x,i) {lookupPartA[x.id] = i;});
Now, you can loop quickly with the listOfPartA array, but also find quickly:
listOfPartA[lookupPartA[34523]]
Pre-index your object:
var lookupPartA = {
34523: { foo: 7, bar: 123},
...
};
var listOfIds = Object.keys(lookupPartA);
Now, you can find ids quickly in the lookupPartA object, but also loop quickly:
for (var i=0, l=listOfIds.length; i<l; i++)
lookupPartA[listOfIds[i]]
According to theses tests, arrays are always fasters than objects.
array vs object performance test
object vs array vs native linked list
benchmark comparison

Concatenate two JSON objects

I have two JSON objects with the same structure and I want to concat them together using Javascript. Is there an easy way to do this?
Based on your description in the comments, you'd simply do an array concat:
var jsonArray1 = [{'name': "doug", 'id':5}, {'name': "dofug", 'id':23}];
var jsonArray2 = [{'name': "goud", 'id':1}, {'name': "doaaug", 'id':52}];
jsonArray1 = jsonArray1.concat(jsonArray2);
// jsonArray1 = [{'name': "doug", 'id':5}, {'name': "dofug", 'id':23},
//{'name': "goud", 'id':1}, {'name': "doaaug", 'id':52}];
If you'd rather copy the properties:
var json1 = { value1: '1', value2: '2' };
var json2 = { value2: '4', value3: '3' };
function jsonConcat(o1, o2) {
for (var key in o2) {
o1[key] = o2[key];
}
return o1;
}
var output = {};
output = jsonConcat(output, json1);
output = jsonConcat(output, json2);
Output of above code is{ value1: '1', value2: '4', value3: '3' }
The actual way is using JS Object.assign.
Object.assign(target, ...sources)
MDN Link
There is another object spread operator which is proposed for ES7 and can be used with Babel plugins.
Obj = {...sourceObj1, ...sourceObj2}
I use:
let x = { a: 1, b: 2, c: 3 }
let y = {c: 4, d: 5, e: 6 }
let z = Object.assign(x, y)
console.log(z)
// OUTPUTS:
{ a:1, b:2, c:4, d:5, e:6 }
From here.
You can use jquery extend method.
Example:
o1 = {"foo":"bar", "data":{"id":"1"}};
o2 = {"x":"y"};
sum = $.extend(o1, o2);
Result:
sum = {"foo":"bar", "data":{"id":"1"}, "x":"y"}
One solution is to use a list/array:
var first_json = {"name":"joe", "age":27};
var second_json = {"name":"james", "age":32};
var jsons = new Array();
jsons.push(first_json);
jsons.push(second_json);
Result
jsons = [
{"name":"joe", "age":27},
{"name":"james", "age":32}
]
if using TypeScript, you can use the spread operator (...)
var json = {...json1,...json2}
You can use Object.assign() method. The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.[1]
var o1 = { a: 1 }, o2 = { b: 2 }, o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
okay, you can do this in one line of code. you'll need json2.js for this (you probably already have.). the two json objects here are unparsed strings.
json1 = '[{"foo":"bar"},{"bar":"foo"},{"name":"craig"}]';
json2 = '[{"foo":"baz"},{"bar":"fob"},{"name":"george"}]';
concattedjson = JSON.stringify(JSON.parse(json1).concat(JSON.parse(json2)));
Just try this, using underscore
var json1 = [{ value1: '1', value2: '2' },{ value1: '3', value2: '4' }];
var json2 = [{ value3: 'a', value4: 'b' },{ value3: 'c', value4: 'd' }];
var resultArray = [];
json1.forEach(function(obj, index){
resultArray.push(_.extend(obj, json2[index]));
});
console.log("Result Array", resultArray);
Result
var baseArrayOfJsonObjects = [{},{}];
for (var i=0; i<arrayOfJsonObjectsFromAjax.length; i++) {
baseArrayOfJsonObjects.push(arrayOfJsonObjectsFromAjax[i]);
}
I use:
let jsonFile = {};
let schemaJson = {};
schemaJson["properties"] = {};
schemaJson["properties"]["key"] = "value";
jsonFile.concat(schemaJson);
The simplest way :
const json1 = { value1: '1', value2: '2' };
const json2 = { value2: '4', value3: '3' };
const combinedData = {
json1,
json2
};
console.log(combinedData)
I dont know if you want this:
U can use this for create from arrays, all arrays need contains the same number of elments.
Example:
If you have:
let a = ["a", "b", "c"];
let b = [1, 2, 3];
Use
concatArraysLikeJson([a, b]);
The result of is:
let result = {
0 : ["a", 1],
1 : ["b", 2],
2 : ["c", 3]
};
Typescript
concatArraysLikeJson(arrays:any){
let result:any = {};
let size:number = 0;
let make:boolean = true;
if(arrays.length > 0){
size = arrays[0].length;
for(let i = 1; i < arrays.length; i++){
let array = arrays[i];
if(make){
if(array.length != size){
make = false;
}
}
}
}
if(make){
for (let o = 0; o < size; o++) {
result[o] = [];
}
for(let i = 0; i < arrays.length; i++){
const array = arrays[i];
//console.log(array);
for (let o = 0; o < size; o++) {
const element = array[o];
result[o].push(element);
}
}
return result;
}else{
return false;
}
}
Javascript:
concatArraysLikeJson(arrays){
let result = {};
let size = 0;
let make = true;
if(arrays.length > 0){
size = arrays[0].length;
for(let i = 1; i < arrays.length; i++){
let array = arrays[i];
if(make){
if(array.length != size){
make = false;
}
}
}
}
if(make){
for (let o = 0; o < size; o++) {
result[o] = [];
}
for(let i = 0; i < arrays.length; i++){
const array = arrays[i];
//console.log(array);
for (let o = 0; o < size; o++) {
const element = array[o];
result[o].push(element);
}
}
return result;
}else{
return false;
}
}
The JSON Objects and Arrays can be combined in multiple ways within a structure
I can merge json with rules using json-object-merge
import JSONObjectMerge from "json-object-merge";
const target = {
store: {
book: [
{
category: "reference",
author: "Nigel Rees",
title: "Sayings of the Century",
price: 8.95
}
],
bicycle: {
color: "red",
price: 19.95
}
}
};
const source = {
store: {
book: [
{
category: "fiction",
author: "Evelyn Waugh",
title: "Sword of Honour",
isbn: "0-679-43136-5",
price: 12.99
}
]
}
};
const merged = JSONObjectMerge(target, source, { "$.store.book": "PREPEND" });
expect(merged).toEqual({
store: {
book: [
{
// books from source are prepended to the original array
category: "fiction",
author: "Evelyn Waugh",
title: "Sword of Honour",
isbn: "0-679-43136-5",
price: 12.99
},
{
category: "reference",
author: "Nigel Rees",
title: "Sayings of the Century",
price: 8.95
}
],
bicycle: {
color: "red",
price: 19.95
}
}
});

Categories

Resources