Recursively recombining split key/values pairs - javascript

This is making my head hurt, if there are any generous javascript gurus here, i would greatly appreciate some help
What I'm trying to achieve is this:
Given this:
var keys = ["Age", "Name", "Photos", { "Friends": ["FirstName", "LastName"] }];
var values = [ [31, "Bob", ["1.jpg", "2.jpg"], [ ["Bob", "Hope"], ["Foo", "Bar"] ] ], [21, "Jane"] ["4.jpg", "5.jpg"], [ ["Mr", "T"],["Foo", "Bar"] ] ];
I would like to get back this:
var object = [
{
"Age" : 31,
"Name" : "Bob",
"Photos" : ["1.jpg", "2.jpg"]
"Friends": [
{
"FirstName": "Bob",
"LastName" : "Hope"
},
{
"FirstName": "Foo",
"LastName" : "Bar"
}
]
},
{
"Age" : 21,
"Name" : "Jane",
"Photos" : ["4.jpg", "5.jpg"]
"Friends": [
{
"FirstName": "Mr",
"LastName" : "T"
},
{
"FirstName": "Foo",
"LastName" : "Bar"
}
]
}
];
It's for a spec proposal (JsonR) i'm working on here
Currently i'm able to (almost) work this out (but not any deeper..):
var keys = ["Age", "Name", "Photos" ];
var values = [ [31, "Bob", ["1.jpg", "2.jpg"]], [21, "Jane", ["4.jpg", "5.jpg"]] ];
Thank's for any feedback or help!

Here's a function that does what I think you want:
function keyValuesToObject(keys, values) {
var obj = [];
for (var i = 0; i < values.length; i++) {
var value = values[i];
obj.push({});
for (var j = 0; j < value.length; j++) {
var key = keys[j];
if (typeof key === "object") {
for (var k in key) {
obj[i][k] = keyValuesToObject(key[k], value[j]);
}
}
else {
obj[i][key] = value[j];
}
}
}
return obj;
};
It does not handle malformed input, so you might want to put checks in there depending on how you plan to use it.
You can see it in action on this online jsFiddle demo.
By the way, the key and value value arrays you gave had mismatched opening and closing brackets, so I had to fix them.

Fiddle
function pairUpItem(keys, values) {
var len = keys.length;
var result = {};
for (var i = 0; i < len; i++) {
var key = keys[i];
var value = values[i];
if (typeof(key) == "string") {
result[key] = value;
} else {
for (var key2 in key) {
if (key.hasOwnProperty(key2)) {
result[key2] = pairUpItems(key[key2], value);
}
}
}
}
return result;
}
function pairUpItems(keys, values) {
var len = values.length;
var result = [];
for (var i = 0; i < len; i++) {
var value = values[i];
if (typeof(value) !== "undefined") {
result.push(pairUpItem(keys, value));
}
}
return result;
}
var keys = ["Age", "Name", "Photos", { "Friends": ["FirstName", "LastName"] }];
var values = [ [31, "Bob", ["1.jpg", "2.jpg"], [ ["Bob", "Hope"], ["Foo", "Bar"] ] ], [21, "Jane", ["4.jpg", "5.jpg"], [ ["Mr", "T"],["Foo", "Bar"] ] ] ];
var result = pairUpItems(keys, values);
​console.dir(result);​

Related

Object Array Formatting

I have an object in this format:
var request = {
"student": [
[
"name",
"age"
],
[
"Tom",
12
],
[
"Jack",
13
]
]
};
I want to transform it into this:
var request = {
"student": [
{
"name": "Tom",
"age": 12
},
{
"name": "Jack",
"age": 13
}
]
}
I tried doing it this way:
var response = [];
var keysCount = req.result[0].length;
var responseCount = req.result.length - 1;
var i = 0,
j = 0,
key;
for (j = 0; j < responseCount; j++) {
for (i = 0; i < keysCount; i++) {
key = req.result[0][i];
response[j][key] = req.result[j + 1][i];
}
}
return response;
But, it is not working as expected.
It's a matter of looping through the first array and creating an array of objects for all the remaining arrays, using values at matching indexes to create properties on object:
var request = {
"student": [
[
"name",
"age"
],
[
"Tom",
12
],
[
"Jack",
13
]
]
};
// Get the header array
var headers = request.student[0];
// Create the new array but mapping the other entries...
var newArray = request.student.slice(1).map(function(entry) {
// Create an object
var newEntry = {};
// Fill it in with the values at matching indexes
headers.forEach(function(name, index) {
newEntry[name] = entry[index];
});
// Return the new object
return newEntry;
});
console.log(newArray);
I would make a small function tabularize that takes an array of data where the first element is an array of headers, and the remaining elements are the rows
Code that follows uses ES6. If you need ES5 support, you can safely transpile this code using a tool like babel.
// your original data
var request = {
"student": [
[
"name",
"age"
],
[
"Tom",
12
],
[
"Jack",
13
]
]
};
// tabularize function
var tabularize = ([headers, ...rows])=>
rows.map(row=>
headers.reduce((acc,h,i)=>
Object.assign(acc, {[h]: row[i]}), {}));
// your transformed object
var request2 = {student: tabularize(request.student)};
// log the output
console.log(request2);
//=> {"student":[{"name":"Tom","age":12},{"name":"Jack","age":13}]}
Or you can create the request object with the intended shape by passing the tabular data directly into the tabularize function at the time of object creation
// tabularize function
var tabularize = ([headers, ...rows])=>
rows.map(row=>
headers.reduce((acc,h,i)=>
Object.assign(acc, {[h]: row[i]}), {}));
// your request object
var request = {
student: tabularize([
[
"name",
"age"
],
[
"Tom",
12
],
[
"Jack",
13
]
])
};
// log the output
console.log(request);
//=> {"student":[{"name":"Tom","age":12},{"name":"Jack","age":13}]}
Let's start off by writing a little function just to create an object from two arrays, one of keys and one of their values:
function makeObjectFromPairs(keys, values) {
var object = {};
for (var i = 0; i < keys.length; i++) {
object[keys[i]] = values[i];
}
return object;
}
// makeObjectFromPairs(['a', 'b'], [1, 2]) === {a: 1, b: 2}
Now we can use the first element of the students array as the keys, and each of the remaining elements as the values.
var keys = students[0];
var result = [];
for (var i = 1; i < students.length; i++) {
result.push(makeObjectFromPairs(keys, students[i]);
}
You could use Array#map etc. as an alternative for the loops, but perhaps this basic approach is more accessible.
Fixing your original code
Since you made a valiant effort to solve this yourself, let's review your code and see where you went wrong. The key point is that you are not initializing each element in your output to an empty object before starting to add key/value pairs to it.
for (j = 0; j < responseCount; j++) {
// Here, you need to initialize the response element to an empty object.
response[j] = {};
Another solution :
var request = {
"student": [
[
"name",
"age"
],
[
"Tom",
12
],
[
"Jack",
13
]
]
};
var response = {};
var students = [];
var responseCount = request.student.length - 1;
var j = 0,
key;
for (j = 0; j < responseCount; j++) {
var student = {};
request.student[0].forEach(function(name, index) {
student[name] = request.student[1 + j][index];
});
students.push(student)
}
response["students"] = students;
console.log(response); // {"students":[{"name":"Tom","age":12},{"name":"Jack","age":13}]}
Lodash solution
var keys = _.head(request.student);
var valueGroups = _.flatten(_.zip(_.tail(request.student)));
var studentObjects = valueGroups.map(function(values){
return values.reduce(function(obj, value, index){
obj[keys[index]] = value;
return obj;
}, {});
});
console.log(studentObjects);
https://jsfiddle.net/mjL9c7wt/
Simple Javascript solution :
var request = {
"student": [
[
"name",
"age"
],
[
"Tom",
12
],
[
"Jack",
13
]
]
};
var students = [];
for(var x = 1; x<request.student.length;x++)
{
var temp = { 'name' : request.student[x][0],
'age' : request.student[x][1]
}
students.push(temp);
}
request = { 'students' : students}
console.log(request);

How to change the key names to values and values to key names in array of objects in javascript

i have array of objects like this
[{
"First Name": "fname",
"Last Name": "lname"
}, {
"Root cause": "root"
}, {
"Comapany Name": "company"
}]
i want to convert the above array of objects into like this
[{
"fname": "First Name",
"lname": "Last Name"
}, {
"root": "Root cause"
}, {
"company": "Comapany Name"
}]
please can anybody help me on this.
This should do it
var arr = [ {"First Name":"fname", "Last Name":"lname"},
{"Root cause":"root"},
{"Comapany Name":"company"}
];
var newArr = [];
for(var i = 0; i < arr.length; ++i) {
var obj = {};
for(key in arr[i]) {
if (arr[i].hasOwnProperty(key)) {
obj[arr[i][key]] = key;
}
}
newArr.push(obj);
}
You can use Object.keys to get an array of keys in an object. This can be used to access each value in the object.
For example:
var newArray = myArray.map(function(obj){
var keys = Object.keys(obj);
var newObj = {};
keys.forEach(function(key){
var newKey = obj[key];
newObj[newKey] = key;
});
return newObj;
});
You can also use some library to do that, for example underscore has an invert function http://underscorejs.org/#invert.
This code doesn't create a new instance, it just inverts the keys in the same object:
var hash = { 'key1': 'val1', 'key2': 'val2' };
console.log('before', hash)
for (var key in hash) {
hash[hash[key]] = key;
delete hash[key];
}
console.log('after', hash)
This one creates a new object keeping the original unchanged:
var hash = { 'key1': 'val1', 'key2': 'val2' };
console.log('before', hash)
var newHash = {};
for (var key in hash) {
newHash[hash[key]] = key;
}
console.log('new hash inverted', newHash)
You can create a function to reuse the code.
You can do it like this.
var arr1 =[{"First Name":"fname", "Last Name":"lname"},
{"Root cause":"root"},
{"Comapany Name":"company"}
]
var arr2=[];
for(var i=0; i<arr1.length; i++){
var obj = {};
var foo= arr1[i];
for(var key in foo){
obj[foo[key]]=key;
}
arr2.push(obj);
}
console.log(arr2);
Just iterate over all your array of objects and exchanges object values for object keys:
var a = [{"First Name": "fname", "Last Name": "lname"}, {"Root cause": "root"}, {"Comapany Name": "company"}];
a.forEach(function(o) {
for (var key in o) {
o[o[key]] = key;
delete o[key];
}
});
console.log(a);

javascript object manipulation using loop

[{
"name":"John"
"age":19,
"hobby":"Basketball;play computer"
},
{
"name":"Anderson"
"age":19,
"hobby":"Tennis"
}
]
John have 2 hobbies, it suppose to be in array but I have no control of the source of the api. How can I make the json to be below format?
[{
"name":"John"
"age":19,
"hobby":"Basketball"
},{
"name":"John"
"age":19,
"hobby":"play computer"
},
{
"name":"Anderson"
"age":19,
"hobby":"Tennis"
}
]
I'm new to jquery so here's code I've tried :
var hobbies = "";
$.each(json, function(){
hobbies = this.hobby.split(',');
});
var data = [{
"name": "John",
"age": 19,
"hobby": "Basketball;play computer"
}, {
"name": "Anderson",
"age": 19,
"hobby": "Tennis"
}]
$.each(data, function (index, value) {
if (value.hobby.split(';').length > 1) {
var dataArray = value.hobby.split(';');
value.hobby = dataArray[0];
dataArray.shift();
$.each(dataArray, function (innerIndex, innerValue) {
data.push({
"name": value.name,
"age": value.age,
"hobby": innerValue
});
});
}
});
console.log(data);
Fiddle Demo
var arr = [{
"name":"John",
"age":19,
"hobby":"Basketball;play computer"
},
{
"name":"Anderson",
"age":19,
"hobby":"Tennis"
}
];
$.each(arr, function(i,j){
var temp = j.hobby;
var hobby_arr = temp.split(';');
j.hobby = hobby_arr;
});
Try this. However there is an error in your provided json. There should be a ',' after the 'name' value
Here is a fiddle of your working thing ( assuming that ";" is the separator )
http://jsfiddle.net/swaprks/vcpq8dtr/
var json = [{
"name":"John",
"age":19,
"hobby":"Basketball;play computer"
},
{
"name":"Anderson",
"age":19,
"hobby":"Tennis"
}
];
$(function(){
for ( var i = 0; i < json.length; i++ ) {
var obj = json[i];
if ( obj["hobby"].indexOf(";") != -1 ){
var hobbyArr = obj["hobby"].split(";");
for ( var j = 0; j < hobbyArr.length; j++ ){
var newObj = {};
if ( j == 0 ){
json[i]["hobby"] = hobbyArr[j];
} else {
newObj = {
"name": obj["name"],
"age": obj["age"],
"hobby": hobbyArr[j]
}
json.push(newObj);
}
}
}
}
console.log(json)
});

How to map a javascript array to another javascript array

I have a constructor in JavaScript which contains 2 properties Key and Values array:
function Test(key, values) {
this.Key = key;
this.Values = values.map(values);
}
Then I created an array of Test objects:
var testObjectArray = [];
testObjectArray.push(new Test(1, ['a1','b1']), new Test(2, ['a1','b2']));
Now I want to map the testObjectArray to single key-value pair array which will be similar to :
[
{ "Key" : "1", "Value" : "a1" },
{ "Key" : "1", "Value" : "b1" },
{ "Key" : "2", "Value" : "a2" },
{ "Key" : "2", "Value" : "b2" },
]
How can I achieve this using array's map function?
I guess you are misunderstanding map(). Here is a very simple example:
a = [1, 2, 3]
b = a.map(function (i) { return i + 1 })
// => [2, 3, 4]
Here is the MDN documentation for map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map. So you should rethink the usage of map in your case. By the way - your example is not working, because values is not a function.
Here is a possible solution:
res = [];
a = [['a1','b1'],['a1','b2']];
for (var i = 0; i < a.length; ++i) {
for(var j = 0; j < a[i].length; ++j) {
res.push({"Key": i + 1 , "Value" : a[i][j]});
}
}
I'm sure there are other ways, but here's something with plain Javascript that does what you want:
http://jsfiddle.net/KXBRw/
function Test(key, values) {
this.Key = key;
this.Values = values;//values.map(values);
}
function getCombinedTests(testObjectArray) {
var all = [];
for (var i = 0; i < testObjectArray.length; i++) {
var cur = testObjectArray[i];
for (var j = 0; j < cur.Values.length; j++) {
all.push({"Key": ""+cur.Key, "Value": cur.Values[j]});
}
}
return all;
}
var testObjectArray1 = [];
testObjectArray1.push(new Test(1, ['a1','b1']), new Test(2, ['a1','b2']));
var combined = getCombinedTests(testObjectArray1);
console.log(combined);
You could use .reduce(), .concat() and .map() for this.
var result = testObjectArray.reduce(function(res, obj) {
return res.concat(obj.Values.map(function(val) {
return {"Key":obj.Key, "Value":val};
}));
}, []);
Not sure what values.map(values); was supposed to do though.
DEMO: http://jsfiddle.net/BWNGr/
[
{
"Key": 1,
"Value": "a1"
},
{
"Key": 1,
"Value": "b1"
},
{
"Key": 2,
"Value": "a1"
},
{
"Key": 2,
"Value": "b2"
}
]
If you're super strict about not creating unnecessary Arrays, you can tweak it a little and use .push() instead of .concat().
var result = testObjectArray.reduce(function(res, obj) {
res.push.apply(res, obj.Values.map(function(val) {
return {"Key":obj.Key, "Value":val};
}));
return res;
}, []);
DEMO: http://jsfiddle.net/BWNGr/1/
You can achieve this by using the following for each loop where each key value pair will be pushed to an array.
var mapped = [];
$.each(testObjectArray, function(key, value) {
for(x in value.Values) {
mapped.push({
Key: value.Key,
Value: x
});
}
});

JSON to filtered Array in JavaScript

I have this JSON string:
[
{
"pk": "alpha",
"item": [{
"child": "val"
}]
},
{
"pk": "beta",
"attr": "val",
"attr2": [
"child1"
]
},
{
"pk": "alpha",
"anotherkey": {
"tag": "name"
}
}
]
And I need to produce a filtered array without repeated PK, in the example above the last entry: "pk": "alpha","anotherkey": { ... should be eliminated from the output array. All this using JavaScript. I tried with the object JSON.parse but it returns many key,value pairs that are hard to filter for example "key=2 value=[object Object]".
Any help is greatly appreciated.
var data = JSON.parse(jsonString);
var usedPKs = [];
var newData = [];
for (var i = 0; i < data.length; i++) {
if (usedPKs.indexOf(data[i].pk) == -1) {
usedPKs.push(data[i].pk);
newData.push(data[i]);
}
}
// newData will now contain your desired result
var contents = JSON.parse("your json string");
var cache = {},
results = [],
content, pk;
for(var i = 0, len = contents.length; i < len; i++){
content = contens[i];
pk = content.pk;
if( !cache.hasOwnPropery(pk) ){
results.push(content);
cache[pk] = true;
}
}
// restuls
<script type="text/javascript">
// Your sample data
var dataStore = [
{
"pk": "alpha",
"item": [{
"child": "val"
}]
},
{
"pk": "beta",
"attr": "val",
"attr2": [
"child1"
]
},
{
"pk": "alpha",
"anotherkey": {
"tag": "name"
}
}
];
// Helper to check if an array contains a value
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] == obj) {
return true;
}
}
return false;
}
// temp array, used to store the values for your needle (the value of pk)
var tmp = [];
// array storing the keys of your filtered objects.
var filteredKeys = [];
// traversing you data
for (var i=0; i < dataStore.length; i++) {
var item = dataStore[i];
// if there is an item with the same pk value, don't do anything and continue the loop
if (tmp.contains(item.pk) === true) {
continue;
}
// add items to both arrays
tmp.push(item.pk);
filteredKeys.push(i);
}
// results in keys 0 and 1
console.log(filteredKeys);
</script>

Categories