JavaScript “map” function - javascript

Looking at the map method in JavaScript, what am I doing wrong here?
// Input: [ { name: "Kevin"}, { name: "Bob" } ]
// Output: [ { "Kevin" :0 }, { "Bob": 1 } ]
var map = function(arr, property) {
var i = 0;
var m = arr.prototype.map(makeKv);
// Input: { name: "Kevin" }
// Output: { "Kevin" = i } // GLOBAL
function makeKv(item) {
return {
item: i++
};
};
console.log("m : " + m);
};
JSFiddle
Please also help me get rid of the global, too.

There are a few issues here:
First,
var m = arr.prototype.map(makeKv);
You don't need prototype here. You only use that when you are using the constructor, like Array.prototype.map. Here, you just need to do arr.map.
Second,
function makeKv(item) {
return {item: i++};
};
You never declare i anywhere. How can you add one to something that doesn't exist. You need to have var i = 0; before this.
Finally, return {item: i++}; will make a key called literally "item". You need to declare the object first (var ret = {};), then use [item] to set the value.
Array.map's callback is passed the element in the array as the 1st parameter, so item will be an object. You need to do item[property] to get the value you want.
P.S. Don't do "m : " + m in your console.log, that will concat strings, thus converting m to a string. Use , instead: console.log("m : ", m);
So, all together, try:
var map = function(arr, property) {
var i = 0;
var m = arr.map(makeKv);
function makeKv(item) {
var ret = {};
ret[item[property]] = i++;
return ret;
};
console.log("m : ", m);
}
DEMO: http://jsfiddle.net/FgdSj/3/
EDIT: Array.map's callback is passed the index in the array as the 2nd parameter, so var i = 0; isn't needed here:
var map = function(arr, property) {
var m = arr.map(makeKv);
function makeKv(item, index) {
var ret = {};
ret[item[property]] = index;
return ret;
};
console.log("m : ", m);
}
DEMO: http://jsfiddle.net/FgdSj/5/

arr.prototype.map(makeKv);
should be
arr.map(makeKv);
Now you have another issue since it will return
[ { item : 0}, { item : 1} ]
If you change the mapped function to
function makeKv(item) {
var x = {}
x[item.name] = i++;
return x;
};
it would give you what you want.
JSFiddle

Just call .map directly
arr.map(makeKv)
I, for whatever reason (maybe map is overridden), you want to use the Array.prototype's method
[].map.call(arr, makeKv);
Here's, it's all fixed up for you to match your desired output
// input: [{name: "Kevin"}, {name: "Bob"}], "name"
var map = function(arr, property) {
var i = 0;
function makeKv(item) {
var obj = {};
obj[item[property] = i++;
return obj;
};
return arr.map(makeKv);
}
var result = map([{name: "Kevin"}, {name: "Bob"}], "name");
console.log(result);
// [{"Kevin" : 0}, {"Bob" : 1}];

var map = function(arr, property) {
var i = 0;
var m = Array.prototype.map(makeKv);
// input: {name: "Kevin"}
// output: "Kevin" = i // GLOBAL
function makeKv(item) {
return {item: i++};
};
console.log("m : " + m);
return m;
}
map();

Related

How to remove duplicate names from array of objects

var arr = [
{level:0,name:"greg"},
{level:0,name:"Math"},
{level:0,name:"greg"}
];
I have tried the following:
function removeDuplicates:(dataObject){
self.dataObjectArr = Object.keys(dataObject).map(function(key){
return dataObject[key];
});
for(var i= 0; i < self.dataObjectArr.length; i++ ){
self.dataObjectArr[i]['name'] = self.dataObjectArr[i];
self.uniqArr = new Array();
for(var key in self.dataObjectArr){
self.uniqArr.push(self.dataObjectArr[key]);
}
}
self.uniqObject = DataMixin.toObject(self.uniqArr);
return self.uniqObject;
}
But I get error saying: Uncaught TypeError: Converting circular structure to JSON.
You should push the name to an array or a set and check the same in the following..
var arr = [{
level: 0,
name: "greg"
}, {
level: 0,
name: "Math"
}, {
level: 0,
name: "greg"
}]
function removeDuplicates(arr) {
var temp = []
return arr.filter(function(el) {
if (temp.indexOf(el.name) < 0) {
temp.push(el.name)
return true
}
})
}
console.log(removeDuplicates(arr))
Here's a generic "uniquify" function:
function uniqBy(a, key) {
var seen = new Set();
return a.filter(item => {
var k = key(item);
return !seen.has(k) && seen.add(k)
});
}
///
var arr = [
{level:0,name:"greg"},
{level:0,name:"greg"},
{level:0,name:"joe"},
{level:0,name:Math},
{level:0,name:"greg"},
{level:0,name:"greg"},
{level:0,name:Math},
{level:0,name:"greg"}
];
uniq = uniqBy(arr, x => x.name);
console.log(uniq);
See here for the in-depth discussion.
I believe you have a syntax error " removeDuplicates:(dataObject){ ..."
should be without the ":" >> " removeDuplicates(dataObject){ ... "
"
You can try this :
function removeDuplicates(arr){
var match={}, newArr=[];
for(var i in arr){ if(!match[arr[i].name]){ match[arr[i].name]=1; var newArr=i; } }
return newArr;
}
arr = removeDuplicates(arr);
You can use $.unique(), $.map(), $.grep()
var arr = [
{level:0,name:"greg"},
{level:0,name:"Math"},
{level:0,name:"greg"}
];
var res = $.map($.unique($.map(arr, el => el.name)), name =>
$.grep(arr, el => el.name === name)[0]);
jsfiddle https://jsfiddle.net/4tex8xhy/3
Or you can use such libraries as underscore or lodash (https://lodash.com/docs/4.16.2). Lodash example:
var arr = [
{level:0,name:"greg"},
{level:0,name:"Math"},
{level:0,name:"greg"}
];
var result = _.map(_.keyBy(arr,'name'));
//result will contain
//[
// {
// "level": 0,
// "name": "greg"
// },
// {
// "level": 0,
// "name": "Math"
// }
//]
Ofc. one thing to always consider in these tasks, what do you want exactly are you going to do: modify an existing array, or get a new one back. This example returns you a new array.

merging two array of objects with custom compare function with javascript

I have two array of objects like:
var A = [{title:"name1",count:5},{title:"name2",count:1},{title:"name3",count:3}];
and:
var B = [{title:"name2",count:7},{title:"name3",count:2},{title:"name4",count:3},{title:"name5",count:8}];
I need to merge this two array in one array and sum the "count" values in returned array when the "title" properties is same:
the last answer must be:
[{title:"name1",count:5},{title:"name2",count:8},{title:"name3",count:5},{title:"name4",count:3},{title:"name5",count:8}]
how can i do this???
You can use Array#forEach and Array#some to achieve a result
var M = A.concat(B)
var C = [];
M.forEach(function(a) {
var index;
if (C.some(function(c, i) { index = i; return a.title == c.title; })) {
C[index].count += a.count;
} else {
C.push(a);
}
});
console.log(C); // as you expect
Solution with Array.concat and Array.map functions:
var merged = A.concat(B), titles = [], result = [];
merged.map(function(obj){
if (titles.indexOf(obj.title) === -1) {
titles.push(obj.title);
result.push(obj);
} else {
result[titles.indexOf(obj.title)]['count'] += obj['count'];
}
});
console.log(result); // will output the expected array of objects
It can be done like this https://jsfiddle.net/menm9xeo/
var noMatch;
var A = [{title:"name1",count:5},{title:"name2",count:1},{title:"name3",count:3}];
var B = [{title:"name2",count:7},{title:"name3",count:2},{title:"name4",count:3},{title:"name5",count:8}];
//for each A, loop through B's. If a match is found combine the Counts in A.
for(var i=0;i<A.length;i++){
for(var j=0;j<B.length;j++){
if(A[i].title == B[j].title){
A[i].count += B[j].count;
}
}
}
//find all B's that were not combined with A in the previous step, and push them into A.
for(var i=0;i<B.length;i++){
noMatch = true;
for(var j=0;j<A.length;j++){
if(B[i].title == A[j].title){
B[i].count += A[j].count;
noMatch = false;
}
}
if(noMatch){A.push(B[i]);}
}
Heres a simple 3 line answer (minus the A/B vars); utilizes the fact that objects must have unique keys
var A = [{title:"name1",count:5},{title:"name2",count:1},{title:"name3",count:3}];
var B = [{title:"name2",count:7},{title:"name3",count:2},{title:"name4",count:3},{title:"name5",count:8}];
var o = {};
A.concat(B).forEach(function(a){o[a.title] = o.hasOwnProperty(a.title)? o[a.title]+a.count: a.count});
var AB = Object.keys(o).map(function(j){ return {title:j,count:o[j]} });
This proposal is merging and counting with a temporary object and Array#forEach()
The forEach() method executes a provided function once per array element.
var arrayA = [{ title: "name1", count: 5 }, { title: "name2", count: 1 }, { title: "name3", count: 3 }],
arrayB = [{ title: "name2", count: 7 }, { title: "name3", count: 2 }, { title: "name4", count: 3 }, { title: "name5", count: 8 }],
result = function (array) {
var o = {}, r = [];
array.forEach(function (a) {
if (!(a.title in o)) {
o[a.title] = { title: a.title, count: 0 };
r.push(o[a.title]);
}
o[a.title].count += a.count;
});
return r;
}(arrayA.concat(arrayB));
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Using lodash ._concat function :
var result = _.concat(A, B);
Fiddle

Generate a nested object structure by string key/path

I want to make a function called createAssociativeArray which will recive two parameters: string and object, like this:
function createAssociativeArray(string, object) {
//...
}
The last item of string should get the object data. See an use/return example:
createAssociativeArray('key1.key2.key3', {
data1: 1,
data2: 2,
data3: 3
});
// key1: {
// key2: {
// key3: {
// data1: 1,
// data2: 2,
// data3: 3
// }
// }
// }
What's the most simple and robust method to do it?
Use eval isn't is a possibility.
What I was tried:
function createAssociativeArray(string, object) {
string = string.split('.');
return string.reduce(function(_object, _target, i) {
_object[_target] = (i + 1 === string.length ? object : {});
return _object;
}, {});
}
It didn't produced the expected result because the object is reseted to {}.
[JSFiddle]
Here's what I came up with:
function createAssociativeArray(string, object) {
var parts = string.split('.');
var last = parts[parts.length - 1];
var tree = {};
var node = parts.slice(0, -1).reduce(function (memo, current) {
return (memo[current] = {});
}, tree);
node[last] = object;
return tree;
}
I was curious to see if I could make a recursive solution, so here it is:
function createAssociativeArray(string, object) {
if (string === "") return object;
var stringarr = string.split('.');
var laststr = stringarr.pop();
var newobj = {};
newobj[laststr] = object;
return createAssociativeArray(stringarr.join("."), newobj);
}
Working JSFiddle demo: https://jsfiddle.net/pt352dxg/
Possible implementation:
Working demo
function createChain(keys, value) {
var obj = {};
var target = obj;
keys = keys.split('.');
keys.forEach(function(key, index) {
target = target[key] = index === keys.length - 1 ? value : {};
});
target = value;
return obj;
}
This function actually can accept an optional existing Object ({k:2, kk: 3, key1: 4}) and merge that with given json path. e.g. Try on chrome debugger console:
JSON.stringify(createAssociativeArray('key1.key2.key3', { data1: 1, data2: 2, data3: 3}, {k:2,kk:3, key1:{}}))
will print this:
"{"k":2,"kk":3,"key1":{"key2":{"key3":{"data1":1,"data2":2,"data3":3}}}}"
..
function createAssociativeArray(key, value, data) {
if(!finalData && data)
finalData = data;
var finalData;
if (!data)
data = finalData = {};
var keys = key.split('.');
if (keys.length < 2) {
data[keys[0]] = value;
} else {
if (!data[keys[0]])
data[keys[0]] = {};
data = data[keys.shift()];
createAssociativeArray(keys.join("."),value,data);
}
return finalData;
};
You were pretty close in your original attempt.
function createAssociativeArray(string, object) {
return string.split('.').reverse().reduce(function (inner, key) {
var outer = {};
outer[key] = inner;
return outer;
}, object);
}
http://jsfiddle.net/xewoa06t/
This worked for me:
function createAssociativeArray(string, object){
var array = string.split('.');
var aArray = {};
if(array.length > 1){
aArray[array[array.length - 1]] = object;
array.splice(array.length - 1, 1);
createAssociativeArray(array.join('.'), aArray)
}else{
aArray[array[array.length - 1]] = object;
return aArray
}
};
createAssociativeArray('key1.key2.key3', {data1: 1, data2: 2, data3: 3});
Basically, builds object from ground up, starting with the original object, then wrapping the 'layers' around it recursively
Nice case for a recursive function!
function createAssociativeArray(string, object) {
if (string.split('.').length == 1) {
var outObj = {};
outObj[string] = object;
return outObj;
} else {
var outObj = {};
outObj[string.split('.')[0]] = createAssociativeArray(string.split('.').slice(1).join('.'), object);
return outObj;
}
}
It's easier with a simple loop, the key point is doing in reverse (like #JustcallmeDrago)
function createAssociativeArray(keys, data)
{
var temp, keyPart
for(keys = keys.split('.'); keys.length; data = temp)
{
keyPart = keys.pop()
temp = {}
temp[keyPart] = data
}
return data
}
// TEST
x = createAssociativeArray("key1.key2.key3", { data1: "value1", data2: "value2" })
document.write('<pre>'+x+'\n'+x.key1 +'\n'
+x.key1.key2 + '\n'
+x.key1.key2.key3 +'\n'
+x.key1.key2.key3.data1 +'\n'
+x.key1.key2.key3.data2 +'</pre>')
Since no one have proviced a while-loop solution:
function namespace(path, context) {
var obj = context;
var s = path.split('.');
var p;
while (s.length) {
p = s.shift();
obj = obj[p] || (obj[p] = {});
}
return context;
}
ES6 one liner
(str, obj) => str.split('.').reverse().reduce((inner, key) => ({[key]: inner}), obj);

Merge array of javascript objects by property key

First of all: I already found this thread, which basically is exactly what I want, but I tried my best to apply it to my needs - I couldn't.
So, I have the following javascript function:
function loadRelationData(object) {
var result = [];
var parents = []
parents = getParentObjectsByObjectID(object['ObjectID']);
var tmpFirstObjects = [];
var tmpOtherObjects = [];
$.each(parents, function (_, parent) {
var keyName = 'Übergeordnete ' + parent['ObjectType'];
var pushObject = {};
if (parent['ObjectType'] == object['ObjectType']) {
pushObject['Fieldname'] = keyName;
pushObject['Value'] = parent['Name'];
tmpFirstObjects.push(pushObject);
} else {
pushObject['Fieldname'] = keyName;
pushObject['Value'] = parent['Name'];
tmpOtherObjects.push(pushObject);
}
});
result = result.concat(tmpFirstObjects).concat(tmpOtherObjects);
return result;
}
The parents array looks like this
And my function creates this result
This might be a bit complicated, but I need to split it up like this, because I need the order.
What I want is an array with both "TEC_MapLocations" joined together like this:
[
{Fieldname: 'Übergeordnete TEC_Equipment', Value: 'E0192'},
{Fieldname: 'Übergeordnete TEC_MapLocation', Value: ['M100', 'M200']},
{Fieldname: 'Übergeordnete TEC_FunctionalLocation', Value: 'FL456'}
]
Any ideas on how to alter my code to achieve the desired result right away or how to merge the results array?
edit: I used Joseph's solution and used the following (quick and dirty) sort function to get back my desired sorting:
output.sort(function (a, b) {
if (a.ObjectType == object.ObjectType) {
return -1
} else {
return 1
}
});
What you'd want to do first is build a hash with Fieldname as key, and an array as value. Then you'd want to use reduce to add the values into the hash and array. Then you can transform it into an array using Object.keys and map.
var input = [
{Name: 'M100', ObjectID: 1, ObjectType: 'TEC_MapLocation'},
{Name: 'M200', ObjectID: 2, ObjectType: 'TEC_MapLocation'},
{Name: 'FL456', ObjectID: 4, ObjectType: 'TEC_FunctionalLocation'},
{Name: 'E0192', ObjectID: 5, ObjectType: 'TEC_Equipment'}
];
var hash = input.reduce(function(carry, item){
// Create the name
var name = 'Übergeordnete ' + item.ObjectType;
// If array with name doesn't exist, create it
if(!carry[name]) carry[name] = [];
// If item isn't in the array, add it.
if(!~carry[name].indexOf(item.Name)) carry[name].push(item.Name);
return carry;
}, {});
// Convert the hash into an array
var output = Object.keys(hash).map(function(key, index, array){
return { Fieldname: key, Value: hash[key] }
});
document.write(JSON.stringify(output));
Try this:
function joinObjects( array ) {
// Start with empty array
var ret = new Array();
// Iterate array
for ( var i = 0; i < array.length; i++ ) {
// Search by fieldname
var match = false;
var j;
for ( j = 0; j < ret.length; j++ ) {
if ( array[i].Fieldname == ret[j].Fieldname ) { match = true; break; }
}
// If not exists
if ( !match ) {
// Intert object
ret.push({
Fieldname: array[i].Fieldname,
Value: new Array()
});
}
// Insert value
ret[j].Value.push( array[i].Value );
}
// Return new array
return ret;
}
http://jsfiddle.net/6entfv4x/

How to do lazy evaluation of an array being processed by a for loop

window.onload = function () {
x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:x, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = myArray[i].a + myArray[i].b;
}
alert(x); // alerts '';
}
Hi, the above is an example of what I'm trying to do. Basically, I would like for x to be evaluated after the 2nd array element computes it. I think this is called lazy evaluation, but not sure... I'm somewhat new.
How can I process my array in the loop and x be evaluated each time such that when I get to the third iteration, x = 'cd' and will alert as 'cd'?
I think I figured out the answer with your help and the other thread I mentioned in the comment. Just need to wrap x in a function and then define a get function to apply to all elements:
window.onload = function () {
function get(e) {return (typeof e === 'function') ? e () : e; }
var x = '';
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, {a:function() {return x; }, b:''} ];
for (i = 0; i < myArray.length; i += 1) {
x = get(myArray[i].a) + get(myArray[i].b);
}
alert(x); // alerts 'cd';
}
x can be anything then. For example (x + 'xyz') will alert 'cdxyz'. So this way I can have any variable that I want evaluated later (when needed) be evaluated correctly (based on state at that point).
That's what I needed. :)
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "ace"
You can use a closure so you can't access the values, like:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function make_getter(list) {
return {
get: function (num) {
var i, agg = { a: "", b: "" };
for (i = 0; i <= num; i += 1) {
agg.a += list[i].a;
}
return agg;
}
};
}
var getter = make_getter(elements);
console.log(getter.get(0).a); // "a"
console.log(getter.get(1).a); // "ac"
console.log(getter.get(2).a); // "ace"
You can make different implementations of the aggregation function.
With recursion:
var elements = [ { a:"a", b:"b"}, {a:"c", b:"d"}, {a:"e", b:"f"} ];
function getter(list, num) {
var i, agg = list[num];
if (num > 0) {
agg.a = getter(list, num-1).a + agg.a;
}
return agg;
}
console.log(getter(elements, 0).a); // "a"
console.log(getter(elements, 1).a); // "ac"
console.log(getter(elements, 2).a); // "aace" <-- note, elements are actually modified!
console.log(getter(elements, 2).a); // "aaacaace" <-- note, elements are actually modified!
old answer
Since x is not an object it's value will be copied, rather than passed as a reference.
If you change your code to:
var element = { a: '', b:'' };
myArray = [ {a:'a', b:'b'}, {a:'c', b:'d'}, element ];
for (i = 0; i < myArray.length; i += 1) {
element.a = myArray[i].a + myArray[i].b;
}
alert(el.a); // alerts 'cd';
You will get "cd".
This is not called lazy evaluation by the way. It's just an aggregate or something.

Categories

Resources