I want to transform this to an array of objects ordered based on an array of keys:
{
tom: 11,
jim: 22,
jay: 13
}
Input -> Output examples:
['jim', 'tom', 'kim', 'jay'] -> [{jim: 22}, {tom: 11}, {jay: 13}]
['may', 'jay', 'tom', 'jim'] -> [{jay: 13}, {tom: 11}, {jim: 22}]
How can I accomplish this? I'd rather a one line lodash solution.
For what it is worth, here's a JS function that performs what you want.
function transform(targetObj, keyArray) {
let resultArray = [];
// Sanity (may want to throw error here)
if (!targetObj || !keyArray) { return resultArray; }
for (let i = 0; i < keyArray.length; i++) {
if (!targetObj.hasOwnProperty(keyArray[i])) { continue; }
let item = {};
item[keyArray[i]] = targetObj[keyArray[i]];
resultArray.push(item);
}
return resultArray;
}
Tests
let obj = { tom: 11, jim: 22, jay: 13 };
let input1 = ['jim', 'tom', 'kim', 'jay'];
let input2 = ['may', 'jay', 'tom', 'jim'];
let result1 = transform(obj, input1);
console.log(JSON.stringify(result1));
let result2 = transform(obj, input2);
console.log(JSON.stringify(result2));
JSFiddle
Assuming the object is named obj and the list of keys to look up is named keys:
_.zip([keys, keys.map(x => obj[x])]).filter(([k, v]) => v).map(x => _.fromPairs([x]))
Or, if you don't care about order, there's another way:
_.toPairs(obj).filter(([k, v]) => _.include(keys, k)).map(x => _.fromPairs([x]))
A one-liner lodash solution would be to use lodash#toPairs to convert the object into an array of key-value pairs, lodash#chunk to wrap each pairs with another array as preparation for mapping each item to form the pairs into an object using lodash#map with a lodash#fromPairs iteratee.
var result = _(data).toPairs().chunk().map(_.fromPairs).value();
var data = {
tom: 11,
jim: 22,
jay: 13
};
var result = _(data).toPairs().chunk().map(_.fromPairs).value();
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
An Vanilla JS ES6 solution would be to use Object#keys to get an array of keys from the object, Array#map to map each keys to associate each key to form an array of objects. We used the ES6 computed property names feature to associate the keys directly into an anonymous object.
var result = Object.keys(data).map(key => ({ [key]: data[key] }));
var data = {
tom: 11,
jim: 22,
jay: 13
};
var result = Object.keys(data).map(key => ({ [key]: data[key] }));
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
If the browsers that you use doesn't support ES6 then you can simply convert the solution above into this:
var result = Object.keys(data).map(function(key) {
var object = {};
object[key] = data[key];
return object;
});
var data = {
tom: 11,
jim: 22,
jay: 13
};
var result = Object.keys(data).map(function(key) {
var object = {};
object[key] = data[key];
return object;
});
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
Basically for a one liner solution to this you don't need lodash, just simple filter and map would be enough, however if you want you can use lodash.
//this is your source object
var keys = {
tom: 11,
jim: 22,
jay: 13
}
Now, Lets assume you have 2 (some) arrays
var input1 = ['jim', 'tom', 'kim', 'jay'];
var input2 = ['may', 'jay', 'tom', 'jim'];
And here we go:
//for first input
input1.filter(s=>keys[s]).map(s=> ({[s]: keys[s]}));
//for secondinput
input2.filter(s=>keys[s]).map(s=> ({[s]: keys[s]}));
Here is a working snippet for you:
var keys = {
tom: 11,
jim: 22,
jay: 13
}
var input1 = ['jim', 'tom', 'kim', 'jay'];
var input2 = ['may', 'jay', 'tom', 'jim'];
var output1 = input1.filter(s=>keys[s]).map(s=> ({[s]: keys[s]}));
var output2 = input2.filter(s=>keys [s]).map(s=> ({[s]: keys[s]}));
console.log("Output1: ", JSON.stringify(output1));
console.log("Output2: ", JSON.stringify(output2));
Related
I need to convert this object
{
person1_name: "John",
person0_name: "Rob",
person1_age: 36,
person0_age: 45,
total: 2,
}
into this format
{
mambers: [
{
name: "Rob",
age: 45,
},
{
name: "John",
age: 36,
},
],
total: 2,
}
Does anyone know how to do it in not to complicated way using JavaScript?
Following is one of the way.
const x={
person1_name: 'John',
person0_name: 'Rob',
person1_age: 36,
person0_age: 45,
total: 2
}
const extractKey = keyName => obj => Object.keys(obj).filter(key => key.includes(keyName))
const names = extractKey('_name')(x);
const ages = extractKey('_age')(x);
const transform = (names, ages) => obj => ({ members: names.map((key, index) => ({name:obj[key], age: obj[ages[index]]})), total:names.length})
console.log(transform(names, ages)(x))
Asuming that there exists for every 0, 1, ... total-1 an entry for name and age you can use this:
let data = {
person1_name: "John",
person0_name: "Rob",
person1_age: 36,
person0_age: 45,
total: 2,
};
let result = {members: [], total: data.total};
for (let i=0; i<data.total; i++) {
result.members.push({
name: data['person' + i + '_name'],
age: data['person' + i + '_age'],
});
}
console.log(result);
Assuming all the keys are in the format of 'person{index}_{name}', i.e. 'person0_name', a regex approach can be taken, as such:
var input = {
person1_name: "John",
person0_name: "Rob",
person1_age: 36,
person0_age: 45,
total: 2,
};
function transform(input) {
var output = {};
var members = {};
var keys = Object.keys(input);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var match = key.match(/person(.*[^_])_(.*)/);
if (!match) {
output[key] = input[key];
continue;
}
var member = members[match[1]] || {};
member[match[2]] = input[key];
members[match[1]] = member;
}
output.members = Object.keys(members).map((k) => members[k]);
return output;
}
console.log(transform(input));
This function can accommodate more than just 'name' and 'age', so if additional properties need transforming this will work.
A solution not requiring contiguous numbering:
var input = {
person1_name: "John",
person0_name: "Rob",
person1_age: 36,
person0_age: 45,
total: 2,
}
// match N from personN_name, using lookbehind and lookahead to match only the number
// use "optional chaining index" ?.[0] to extract the first match is found
const match = key => key.match(/(?<=^person)\d+(?=_name$)/)?.[0]
// "functional" approach
// extract keys from input, map them with the matching regexp and filter actual matches
var ids = Object.keys(input).map(k => match(k)).filter(Boolean)
// map ids to {name,age} objects
var members = ids.map(id => ({name:input[`person${id}_name`],age:input[`person${id}_age`]}))
// construct output
var output = {total:input.total,members}
console.log(output)
// "iterative" pproach
// initial value of outpit
var output = {total:input.total,members:[]}
for (var [k,v] of Object.entries(input)) { //for each key-value from input
var id = match(k) // try to get key
if (!id) continue // if id not found, continue to next kev-value pair
var name = v // the match is against personN_name, so v is the name
var age = input[`person${id}_age`] // get age
output.members.push({name,age}) // push new member
}
console.log(output)
I have 2 array, one for key and other for value.
Want create new array with these arrays.
key: [01, 02, 03]
value: ["hi", "hello", "welcome"]
Output I need:
[
{"key": "1","value":"hi"},
{"key": "2","value":"hello"},
{"key": "3","value":"welcome"}
]
How to get result by this way.?
My code:
output = key.map(function(obj, index){
var myObj = {};
myObj[value[index]] = obj;
return myObj;
})
Result:
[
{"1","hi"},
{"2","hello"},
{"3","welcome"}
]
const keys = [01, 02, 03];
const values = ['hi', 'hello', 'welcome'];
const res = keys.map((key, ind) => ({ 'key': ''+key, 'value': values[ind]}));
console.log(res);
There is also a proposal for the following method of Object, fromEntries, which will do exactly what you want to, but it is not supported yet by the major browsers:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
var myArray = [];
var keys = [45, 4, 9];
var cars = ["Saab", "Volvo", "BMW"];
cars.forEach(myFunction);
var txt=JSON.stringify(myArray);
document.getElementById("demo").innerHTML = txt;
function myFunction(value,index,array) {
var obj={ key : keys[index], value : value };
myArray.push(obj);
}
<p id="demo"></p>
You could take an object with arbitrary count of properties amd map new objects.
var key = [1, 2, 3],
value = ["hi", "hello", "welcome"],
result = Object
.entries({ key, value })
.reduce((r, [k, values]) => values.map((v, i) => Object.assign(
{},
r[i],
{ [k]: v }
)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here you have another apporach using reduce():
let keys = [01, 02, 03];
let values = ['hi', 'hello', 'welcome'];
let newArray = keys.reduce((res, curr, idx) => {
res.push({'key': curr.toString(), 'value': values[idx]});
return res;
}, []);
console.log(newArray);
I have
var tab = {
abc:1,
def:40,
xyz: 50
}
I want to change the name of abc,def, xyz to something else, is it possible?
I tried
const test = Object.keys(tab).map(key => {
if (key === 'abc') {
return [
a_b_c: tab[key]
]
}
});
console.log(test);
I got many undefined keys.
Here is the full code for replacing keys based on object that maps the values to replace:
const tab = {abc: 1, def: 40, xyz: 50};
const replacements = {'abc': 'a_b_c', 'def': 'd_e_f'};
let replacedItems = Object.keys(tab).map((key) => {
const newKey = replacements[key] || key;
return { [newKey] : tab[key] };
});
This will output an array with three objects where keys are replaced. If you want to create a new object out of them, just:
const newTab = replacedItems.reduce((a, b) => Object.assign({}, a, b));
This outputs: {"a_b_c": 1, "d_e_f": 40, "xyz": 50}
Here's how I solved it. I used a map to map between existing key and new key. Just substitute the map with whatever new values you need. Finally remove old keys from the object using omit.
var tab = {
abc:1,
def:40,
xyz: 50
}
var map = {
abc : "newabc",
def : "newdef",
xyz : "newxyz"
}
_.each(tab, function(value, key) {
key = map[key] || key;
tab[key] = value;
});
console.log(_.omit(tab, Object.keys(map)));
With lodash mapKeys function its quite easy to transform object keys.
let tab = {
abc: 1,
def: 40,
xyz: 50
}
const map = {
abc: "newabc",
def: "newdef",
xyz: "newxyz"
}
// Change keys
_.mapKeys(tab, (value, key) => {
return map[value];
});
// -> { newabc: 1, newdef: 40, newxyz: 50 }
Here is a way to do it with deconstructing assignment and arrow functions.
const rename = (({abc: a_b_c, ...rest}) => ({a_b_c, ...rest}))
console.log(rename({abc: 1, def: 2}))
// { "a_b_c": 1, "def": 2 }
UPDATE:
Sorry for the syntax errors; corrected and verified in browser console.
Shortest way I've found so far:
let tab = {abc: 1, def: 40, xyz: 50};
const {abc: a_b_c, def: d_e_f, ...rest} = tab;
tab = {a_b_c, d_e_f, ...rest}
// { "a_b_c": 1, "d_e_f": 40, "xyz": 50}
That's easy with lodash.
import { mapKeys } from 'lodash';
const tab = {
abc: 1,
def: 40,
xyz: 50
};
const test = mapKeys(tab, (value, key) => {
if (key === 'abc') return 'a_b_c';
return key;
});
A really easy way to change the keys dynamically without having to map the new key values could work like this :
const tab = { abc: 1, def: 40, xyz: 50 };
const changeString = (s) => s.split('').join('_');
Object.entries(tab).reduce((acc, [k, v]) => ({ ...acc, [changeString(k)]: v }), {})
// Output: {a_b_c: 1, d_e_f: 40, x_y_z: 50}
It uses Object.entries but you can easily swap it over to Object.keys like this:
Object.keys(tab).reduce((acc, k) => ({ ...acc, [changeString(k)]: tab[k] }), {})
Advanced: Nested objects
If you have nested objects and want to cycle through all the keys, you could try this:
const changeString = (s) => s.split('').join('_');
const isArray = (a) => Array.isArray(a);
const isObject = (o) => o === Object(o) && !isArray(o) && typeof o !== 'function';
const keyChanger = (o) => {
if (isObject(o)) {
return Object.entries(o).reduce((acc, [k, v]) => ({ ...acc, [changeString(k)]: keyChanger(v) }), {})
} else if (isArray(o)) {
return o.map((i) => keyChanger(i));
}
return o;
};
And you call it by simply doing this:
const tab2 = { abc: 1, def: 40, xyz: { hij: 12, klm: [{ nop: 43 }, { qrs: 65 }]}}
keyChanger(tab2)
// Output: { a_b_c: 1, d_e_f: 40, x_y_z: { h_i_j: 12, k_l_m: [{ n_o_p: 43 }, { q_r_s: 65 }]}}
You can add the new key and delete the old one.
var tab = {
abc:1,
def:40,
xyz: 50
}
var key = 'abc'
console.log(key)
tab['a_b_c'] = tab[key]
delete tab[key]
console.log(tab);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
you can use ES6 destructuring for it. For example, you can do something like this:
let sample = {a:1,b:2,c:3}
let {a:pippo,...rest} = sample
sample={pippo,...rest}
hope this will help
initial data:
let tab = {
abc: 1,
def: 40,
xyz: 50
};
new key mappings:
let newKeyMappings = {
abc: 'cab',
def: 'fed',
xyz: 'zyx'
};
mapping values with new keys
let mapped = Object.keys(tab).map(oldKey=> {
let newKey = newKeyMappings[oldKey];
let result ={};
result[newKey]=tab[oldKey];
return result;
});
since mapped contains array of mapped object apply reduce operator
let result = mapped.reduce((result, item)=> {
let key = Object.keys(item)[0];
result[key] = item[key];
return result;
}, {});
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);
There is an array of objects
[
{a:1,val:[11,12]},
{a:9,val:[21,22]},
{a:7,val:[31,32]},
{a:8,val:[41,42]}
]
I am trying to convert it into:
[ [{a:1,val:11},{a:9,val:21},{a:7,val:31},{a:8,val:41}] ,
[{a:1,val:12},{a:9,val:22},{a:7,val:32},{a:8,val:42}]
]
How can I use underscore.js chain/map/pluck etc... function to get the flatten result in specified format in the cleanest way?
You could use Array#forEach and build the nested parts upon.
var data = [{ a: 1, val: [11, 12] }, { a: 9, val: [21, 22] }, { a: 7, val: [31, 32] }, { a: 8, val: [41, 42] }],
result = [];
data.forEach(function (a, i) {
a.val.forEach(function (b, j) {
result[j] = result[j] || [];
result[j][i] = { a: a.a, val: b };
});
});
console.log(result);
You can use array's reduce like this
var data = [
{a:1,val:[11,12]},
{a:9,val:[21,22]},
{a:7,val:[31,32]},
{a:8,val:[41,42]}
]
var result = data.reduce((res, next) => {
res[0].push({a: next.a, val: next.val[0]});
res[1].push({a: next.a, val: next.val[1]});
return res;
}, [[], []])
console.dir(result)
I have done as you have requested but used plain ES6 instead of underscore.
var restructure = (x)=>
[x.map(({a,val})=>({a,val:val[0]})),x.map(({a,val})=>({a,val:val[1]}))]
var result = restructure([
{a:1,val:[11,12]},
{a:9,val:[21,22]},
{a:7,val:[31,32]},
{a:8,val:[41,42]}
])
//[[{"a":1,"val":11},{"a":9,"val":21},{"a":7,"val":31},{"a":8,"val":41}],[{"a":1,"val":12},{"a":9,"val":22},{"a":7,"val":32},{"a":8,"val":42}]]
Here's a solution using underscore:
var result = _.chain(data)
.map(item => _.map(item.val, val => ({a: item.a, val})))
.unzip()
.value();
var data = [
{a:1,val:[11,12]},
{a:9,val:[21,22]},
{a:7,val:[31,32]},
{a:8,val:[41,42]}
]
var result = _.chain(data)
.map( item => _.map(item.val, val => ({a: item.a, val})))
.unzip()
.value();
document.getElementById('result').textContent = JSON.stringify(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore.js"></script>
<p>
<pre id="result"></pre>
</p>