Related
I have two arrays as listed below. I'm trying to create a new array of objects by using the field key in array_1 and the values in array_2.
const result = []
array_1 = [{ name: "Color" , field: "color"}, {name: "Shape", field: "shape" }, { name: "Whatever", field: "whatever" }]
array_2 = [["green", "rectangular", "whatever1"], ["yellow", "circle", "whatever2"]]
The result should be:
console.log(result)
// [{color:"green", shape:"rectangular", whatever: "whatever1"},
// { color:"yellow", shape: "circle", whatever:"whatever2"}]
I did this at my final trial:
const rowObj = {}
const result = array.map((subarray) => subarray.map((cell, index) => {
console.log(cell,index)
rowObj[columns[index].field] = cell
return rowObj
}))
Basically, I was overwriting the same object.
Thanks,
One way to do it is to map() over the array_2 and in each iteration:
Create a new object
Iterate over the array_1 to fill the newly created object. You can use the index parameter of the forEach() method's callback function to get the field property from the objects inside array_1.
and then return that object from the callback function of the map() method.
const array_1 = [
{ name: 'Color', field: 'color' },
{ name: 'Shape', field: 'shape' },
{ name: 'Whatever', field: 'whatever' },
];
const array_2 = [
['green', 'rectangular', 'whatever1'],
['yellow', 'circle', 'whatever2'],
];
const result = array_2.map(arr => {
const o = {};
arr.forEach((str, idx) => {
o[array_1[idx].field] = str;
});
return o;
});
console.log(result);
You can use array.map to iterate both arrays and take advantage of Object.fromEntries to build new objects based on the order of array elements:
array_1 = [{ name: "Color" , field: "color"}, {name: "Shape", field: "shape" }, { name: "Whatever", field: "whatever" }]
array_2 = [["green", "rectangular", "whatever1"], ["yellow", "circle", "whatever2"]]
let result = array_2.map(
x => Object.fromEntries(
array_1.map((y,i) => ([y.field, x[i]]))))
console.log(result);
Explanation
You could create a function that creates a constructor based on the descriptions of your object's fields like this:
function createConstructor(fieldsDescriptor) {
return function(fields) {
fieldsDescriptor.forEach((descriptor, index) => {
this[descriptor.field] = fields[index]
})
}
}
Then you could, for example, make a sampleConstructor that creates objects based on the field names of array_1:
const SampleConstructor = createConstructor(array_1)
And then, for each entry in array_2 you could apply your SampleConstructor:
const result = array_2.map(fields => new SampleConstructor(fields))
Motivation
Creating a dedicated constructor adds some clear semantics to your app, shows readers what you are doing and also stores constructor information in the created objects at runtime.
When you later want to know which constructor made which objects you can just call object.constructor and use this information to determine what kind of objects they are.
For example calling result[0].constructor == SampleConstructor will be true because SampleConstructor is the constructor that created the first result.
Demo
Here is a full demo
const array_1 = [{ name: "Color" , field: "color"}, {name: "Shape", field: "shape" }, { name: "Whatever", field: "whatever" }]
const array_2 = [["green", "rectangular", "whatever1"], ["yellow", "circle", "whatever2"]]
function createConstructor(fieldsDescriptor) {
return function(fields) {
fieldsDescriptor.forEach((descriptor, index) => {
this[descriptor.field] = fields[index]
})
}
}
const SampleConstructor = createConstructor(array_1)
const results = array_2.map(fields => new SampleConstructor(fields))
console.log(results)
const EmptyConstructor = createConstructor([])
console.log(results[0].constructor == SampleConstructor)
console.log(results[0].constructor == EmptyConstructor)
You can try this
array_1 = [
{ name: 'Color', field: 'color' },
{ name: 'Shape', field: 'shape' },
{ name: 'Whatever', field: 'whatever' }
];
array_2 = [
['green', 'rectangular', 'whatever1'],
['yellow', 'circle', 'whatever2']
];
const keys = array_1.map(item => item.field);
const output = [];
array_2.forEach(item => {
const temp = {};
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = item[i];
temp[key] = value;
}
output.push(temp);
});
console.log(output);
given this input:
const set1 = new Set([10, "someText", {a: 1, b: 2}]);
const set2 = new Set([10, "someText", {a: 1, b: 2}]);
const set3 = new Set([5, "someText", {a: 3, b: 4}]);
const arr = [set1, set2, set3];
combineDupSets(arr);
Wanted result:
[
Set { 10, 'someText', { a: 1, b: 2 } },
Set { 5, 'someText', { a: 3, b: 4 } }
]
I am writing a function to eliminate all the duplicate sets, and since Set() won't check for duplicates when it's an object or set itself, I wrote the following:
function combineDupSets(arr) {
const hold = [];
arr.forEach(set =>{
const copySet = [...set];
const stringify = JSON.stringify(copySet);
if(hold.indexOf(stringify) === -1) {
hold.push(stringify)
}
})
const end = hold.map(item => JSON.parse(item));
const res = end.map(item => item = new Set(item))
return res;
}
Here, I have to use 3 arrays sized O(n) to check for this, and I was just wondering if there's any other solution that is readable that will be more efficient in checking for this for both time and space complexity?
Thank you
Instead of using indexOf in an array, consider putting the sets onto an object or Map, where the key is the stringified set and the value is the original set. Assuming that the values are in order:
function combineDupSets(arr) {
const uniques = new Map();
for (const set of arr) {
uniques.set(JSON.stringify([...set]), set);
}
return [...uniques.values()];
}
This
iterates over the arr (O(n))
iterates over each item inside once (total of O(n * m) - there's no getting around that)
Iterates over the created Map and puts it into an array (O(n))
If the set values aren't necessarily in order - eg, if you have
Set([true, 'foo'])
Set(['foo', true])
that should be considered equal, then it'll get a lot more complicated, since every item in each Set not only has to be iterated over, but also compared against every other item in every other Set somehow. One way to implement this is to sort by the stringified values:
function combineDupSets(arr) {
const uniques = new Map();
for (const set of arr) {
const key = [...set].map(JSON.stringify).sort().join();
uniques.set(key, set);
}
return [...uniques.values()];
}
You could iterate the sets and check the values and treat object only equal if they share the same object reference.
function combineDupSets(array) {
return array.reduce((r, s) => {
const values = [...s];
if (!r.some(t => s.size === t.size && values.every(Set.prototype.has, t))) r.push(s);
return r;
}, []);
}
const
a = { a: 1, b: 2 },
b = { a: 3, b: 4 },
set1 = new Set([10, "someText", a]),
set2 = new Set([10, "someText", a]),
set3 = new Set([5, "someText", b]),
arr = [set1, set2, set3];
console.log(combineDupSets(arr).map(s => [...s]));
How do I join arrays with the same property value? I cannot map it because it has different indexes.
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
expected output:
var array3 = [
{'label':"label1",'value':"TEXT",'position':0},
{'label':"label2",'value':"SELECT", 'position':1}
];
This is what I did, I cannot make it work,
var arr3 = arr1.map(function(v, i) {
return {
"label": v.label,
"position": v.position,
"value": arr2[?].value
}
});
I think you can use array#reduce to do something like this perhaps:
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
var array3 = array2.reduce((arr, e) => {
arr.push(Object.assign({}, e, array1.find(a => a.label == e.label)))
return arr;
}, [])
console.log(array3);
You could take a Map and check the existence for a new object.
var array1 = [{ label: "label1", position: 0 }, { label: "label3", position: 2 }, { label: "label2", position: 1 }],
array2 = [{ label: "label1", value: "TEXT" }, { label: "label2", value: "SELECT" }],
map = array1.reduce((m, o) => m.set(o.label, o), new Map),
array3 = array2.reduce((r, o) => {
if (map.has(o.label)) {
r.push(Object.assign({}, o, map.get(o.label)));
}
return r;
}, []);
console.log(array3);
.as-console-wrapper { max-height: 100% !important; top: 0; }
As per the effort, we take an assumption that array1 will be having all the labels that are in array2.
Based on that first, create a map for array2and with key being labels. Post that, filter out array1 items which have labels existing in the map and then finally merging the objects of the filtered array and its corresponding values in map extracted from array2.
var array1 = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}];
var array2 = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}];
let map = array2.reduce((a,{label, ...rest}) => Object.assign(a,{[label]:rest}),{});
let result = array1.filter(({label}) => map[label]).map(o => ({...o, ...map[o.label]}));
console.log(result);
Also, in the above snippet, you can improve the performance further by using Array.reduce against filter and map functions to retrieve the result.
var array1 = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}];
var array2 = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}];
let map = array2.reduce((a,{label, ...rest}) => Object.assign(a,{[label]:rest}),{});
let result = array1.reduce((a,o) => {
if(map[o.label]) a.push({...o, ...map[o.label]});
return a;
}, []);
console.log(result);
If you don't know in advance which array(s) will have their labels be a subset of the other (if any), here's a method that allows for either array1 or array2 to have labels that the other array lacks. Use reduce over array1, finding the matching label in array2 if it exists:
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
const output = array1.reduce((a, { label, position }) => {
const foundValueObj = array2.find(({ label: findLabel }) => findLabel === label);
if (!foundValueObj) return a;
const { value } = foundValueObj;
a.push({ label, value, position });
return a;
}, []);
console.log(output);
See Array.prototype.map() and Map for more info.
// Input.
const A = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}]
const B = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}]
// Merge Union.
const mergeUnion = (A, B) => {
const mapA = new Map(A.map(x => [x.label, x]))
return B.map(y => ({...mapA.get(y.label), ...y}))
}
// Output + Proof.
const output = mergeUnion(A, B)
console.log(output)
This works.
Approach: Concatenate the objects with same label, using Object.assign()
var array1 = [{'label':"label1",'position':0},{'label':"label3",'position':2},{'label':"label2",'position':1}];
var array2 = [{'label':"label1",'value':"TEXT"},{'label':"label2",'value':"SELECT"}];
var result = [];
array2.forEach(function(value, index){
result.push(Object.assign({},array1.find(function(v,i){return v.label==value.label}),value));
});
console.log(result)
Im not good with javascript,but you could also do this
var array1 = [
{'label':"label1",'position':0},
{'label':"label3",'position':2},
{'label':"label2",'position':1},
];
var array2 = [
{'label':"label1",'value':"TEXT"},
{'label':"label2",'value':"SELECT"}
];
var array3=[];
for(var i=0;i<array1.length;i++)
{
for(var x=0;x<array2.length;x++)
{
console.log(array1[i]['label'] == array2[x]['label']);
if(array1[i]['label'] == array2[x]['label']){
array3.push({label:array1[i]['label'],value:array2[x]['value'],position:array1[i]['position']});
}
}
}
console.log(array3);
I have multiple collections (like 2 collections but it might be more) in one array like this :
var attributes = [ { colors: [ 10, 20, 30 ] }, { dimensions: [ a, b] } ]
And I want to have somthing like this :
var newArray = [ {10 : a },{ 10 : b },{20 : a},{20 : b},{30 : a},{30 : b} ]
I don't think I understand what you'd like to have in case of more than two items in the outer array, but here's a solution for your example:
var attributes = [{
colors: [10, 20, 30]
}, {
dimensions: ["a", "b"]
}];
var newArray = [];
attributes[0].colors.forEach(color => {
attributes[1].dimensions.forEach(dim => {
var obj = {};
obj[`${color}`] = dim;
newArray.push(obj);
});
});
console.log(newArray);
Additional changes by NewToJS - dimensions: [a, b] To dimensions: ["a", "b"]
If you specify what you want more precisely, I'll try to edit the answer because it all depends on the details.
Definitely depends. But, if the attribute data will always be structured as you describe it, this will do the trick.
var attributes = [ { colors: [ 10, 20, 30 ] }, { dimensions: [ "a","b"] } ]
function crossReference(attributes, key, scope){
let result = [];
let variables = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
if(!key && key !== 0){
attributes.forEach((attr, key)=>{
let keys = Object.keys(attr);
keys.forEach((property)=>{
if(Array.isArray(attr[property])){
console.log(property);
result= result.concat(crossReference(attr[property], key, attributes));
// section.forEach()
}
})
})
}else{
console.log(attributes, scope[key+1], key);
attributes.forEach((attr,index)=>{
if(!scope[key+1]) return;
let next = scope[key+1];
let keys = Object.keys(scope[key+1]);
keys.forEach((property)=>{
if(Array.isArray(next[property])){
next[property].forEach((prop)=>{
result.push({[attr]:prop})
})
}
})
})
}
return result;
}
console.log(crossReference(attributes))
I need to transmit some data, that has too many key-value pairs.
As the keys are similar, I dont want to transmit them with each object.
Consider I have the following data:
[
{
x:11,
y:12
},{
x:21,
y:22
},{
x:31,
y:32
},{
x:41,
y:42
}
];
And I need the final output as
[ [x,y],[[11,12],[21,22],[31,32],[41,42]] ] OR
[ [x,y],[11,12],[21,22],[31,32],[41,42] ]
On the other end, I should be able to convert back to its original form.
It would be great if it can handle an additional key in some of the objects
I think I have seen lodash or underscore function for something close/similar to this, but I'm not able to find it right now.
NOTE: I don't know what the keys will be
Lodash v4.17.1
modify original
var modifiedOriginal = _.chain(original)
.map(_.keys)
.flatten()
.uniq()
.thru(function(header){
return _.concat(
[header],
_.map(original, function(item) {
return _.chain(item)
.defaults(_.zipObject(
header,
_.times(_.size(header), _.constant(undefined))
))
.pick(header)
.values()
.value()
})
);
})
.value();
modified back to original (keys order is not
guarantee)
var backToOriginal = _.map(_.tail(modified), function(item) {
return _.chain(_.head(modified))
.zipObject(item)
.transform(function(result, val, key) {
if (!_.isUndefined(val)) {
result[key] = val;
}
})
.value();
});
JSFiddle code https://jsfiddle.net/wa8kaL5g/1/
Using Array#reduce
var arr = [{
x: 11,
y: 12
}, {
x: 21,
y: 22
}, {
x: 31,
y: 32
}, {
x: 41,
y: 42
}];
var keys = Object.keys(arr[0]);
var op = arr.reduce(function(a, b) {
var arr = keys.reduce(function(x, y) {
return x.concat([b[y]]);
}, [])
return a.concat([arr]);
}, [keys]); //If all the objects are having identical keys!
console.log(JSON.stringify(op));
A little more verbose way of doing it:
[Edit: added the function to convert it back]
function convert(arr) {
var retArr = [ [/* keys (retArr[0]) */], [/* values (retArr[1]) */] ]
arr.forEach(function(obj){
// create new array for new sets of values
retArr[1].push([])
// put all of the keys in the correct array
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
// does the key exist in the array yet?
if (retArr[0].indexOf(key) === -1) {
retArr[0].push(key)
}
// get last index of retArr[1] and push on the values
retArr[1][retArr[1].length - 1].push(obj[key])
}
}
})
return retArr
}
function reConvert(arr) {
var retArr = []
var keys = arr[0]
arr[1].forEach(function(itemArr){
var obj = {}
itemArr.forEach(function(item, i){
obj[keys[i]] = item
})
retArr.push(obj)
})
return retArr
}
var objArr = [
{
x:11,
y:12
},{
x:21,
y:22
},{
x:31,
y:32
},{
x:41,
y:42
}
]
var arrFromObj = convert(objArr)
var objFromArr = reConvert(arrFromObj)
console.log(arrFromObj)
console.log(objFromArr)
A solution using Underscore.
First work out what the keys are:
var keys = _.chain(data)
.map(_.keys)
.flatten()
.uniq()
.value();
Then map across the data to pick out the value for each key:
var result = [
keys,
_.map(data, item => _.map(keys, key => item[key]))
];
and back again:
var thereAndBackAgain = _.map(result[1], item => _.omit(_.object(result[0], item), _.isUndefined));
Lodash's version of object is zipObject and omit using a predicate is omitBy:
var thereAndBackAgain = _.map(result[1], item => _.omitBy(_.zipObject(result[0], item), _.isUndefined));
var data = [
{
x:11,
y:12,
aa: 9
},{
x:21,
y:22
},{
x:31,
y:32,
z: 0
},{
x:41,
y:42
}
];
var keys = _.chain(data)
.map(_.keys)
.flatten()
.uniq()
.value();
var result = [
keys,
_.map(data, item => _.map(keys, key => item[key]))
];
var thereAndBackAgain = _.map(result[1], item => _.omit(_.object(result[0], item), _.isUndefined));
console.log(result)
console.log(thereAndBackAgain)
<script src="
https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
In ES6 you can do it by reducing it with Object.values(), and Object.keys(). You can restore it using a combination of Array.prototype.map() and Array.prototype.reduce():
const convertStructure = (data) => data.reduce((s, item) => {
s[1].push(Object.values(item));
return s;
}, [Object.keys(data[0]), []]); // all objects should be the same, so we can take the keys from the 1st object
const restoreStructure = ([keys, data]) => data.map((item) => item.reduce((o, v, i) => {
o[keys[i]] = v;
return o;
}, {}));
const data = [{
x: 11,
y: 12
}, {
x: 21,
y: 22
}, {
x: 31,
y: 32
}, {
x: 41,
y: 42
}];
const convertedStructure = convertStructure(data);
console.log('convertedStructure:\n', convertedStructure);
const restoredStructure = restoreStructure(convertedStructure);
console.log('restoredStructure:\n', restoredStructure);