Javascript, loop through array and apply other functions - javascript

I have an array of objects,
var out = [{
"type": "1",
"from": "13052033555",
"to": "4444444",
"amount": "40000",
"date": 1461575799,
"status": "1"
}, {
"type": "2",
"from": "13052033555",
"to": "1111111",
"amount": "30000",
"date": 1461575884,
"status": "1"
}...
];
I get only it's values without keys
Now i used this function to get the values from array like this,
I pass array then it returns only values without keys
function foo(a) {
var values = [];
for (var i = 0; i < a.length; i++) {
var obj = a[i];
var arr = Object.keys(obj).map(function(k) {
return obj[k]
});
values.push("[" + arr + "],");
}
return values.join('');
}
Then it returns the values data without keys like this,
["1","13052033555","4444444","40000",1461575799,"1"],
["2","13052033555","1111111","30000",1461575884,"1"],
My question is how can I loop through the values and only apply a method to 5th value of each data array ?

All you have to do in foo is call your fn on arr[4] before continuing with the loop.
arr[4] = fn(arr[4])
This is of course, assuming you don't need to do this after the fact for whatever reason. If that is the case, you can use another for loop like your original code, and just modify the 5th element in each array as specified above, except it would look more like
for (var i = 0; i < outerArray.length; i++) {
outerArray[i][4] = fn(outerArray[i][4])
}
Now that we covered how to do it with your current code, I would also suggest that you don't do this for any real world application. If you want to modify data on a specific object property for a list of objects, you should do it with the object property name (key) and not later on the array of values using an index. This prevents any confusion that could arise from the fact that objects do not have a guaranteed order. Here's an example of how I would do this, assuming you want to modify date:
function foo(a) {
return a.map(function(obj) {
return Object.keys(obj).map(function(k) {
return k === 'date' ? fn(obj[k]) : obj[k]
})
})
}
This way you target the property you want but also don't modify the original object.
Note You should replace fn with your desired function :)
Edit Per your request, here is how you might extend it to check for other property names
function foo(a) {
return a.map(function(obj) {
var values = []
Object.keys(obj).forEach(function(k) {
if (k === 'date') {
values.push(fn(obj[k]))
} else if (k !== 'type') {
values.push(obj[k])
}
})
return values
})
}

var out = [{
"type": "1",
"from": "13052033555",
"to": "4444444",
"amount": "40000",
"date": 1461575799,
"status": "1"
}, {
"type": "2",
"from": "13052033555",
"to": "1111111",
"amount": "30000",
"date": 1461575884,
"status": "1"
}
];
function foo(a) {
var values = [];
for (var i = 0; i < a.length; i++) {
var obj = a[i];
var arr = Object.keys(obj).map(function(k) {
return obj[k]
});
values.push("[" + arr + "],");
}
return values.join('');
}
function myFn(elem){
console.log(elem);
return elem;
}
var arr = foo(out);
arr = JSON.parse("["+arr.substring(0,arr.length-1)+"]")
arr.forEach(function(elem){
return myFn(elem[4]);
});
console.log(arr);

Related

How to make string to json with javascript? (like A//a1,A//a2,A//a3//a31 ..)

How to convert a string to JSON with javascript or jQuery? I've been thinking all day, but I do not get a good idea.
This task is to dynamically create the treeview in the client side (ASP.Net). My idea is to convert the string to an object and convert to JSON type. (String -> object -> JSON) I tried, but the day is gone. It is difficult to construct 2 more depth like A->a3->a31.
String is
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
and JSON format is
{
"title": "A",
"key": "1",
"folder": true,
"children": [{
"title": "a1",
"key": "2"
}, {
"title": "a2",
"key": "3"
}, {
"title": "a3",
"key": "4",
"folder": true,
"children": [{
"title": "a31",
"key": "5"
}...
}]
}
(This is fancytreeview plugin)
‘//‘ is depth and ‘,’ is split.
Please help me..
Edit)
I want to turn ‘sString’ to JSON format.. but It’s ok just JSON type string.
Please understand that my sentence is strange because my native language is not English.
Edit2)
oh.. I want to convert the string to an object and then convert it back to JSON format. I do not have the confidence to convert that string into JSON format right away. Because there are more than 8000 variants. If It’s can, let me know how.
I believe this can be done without recursion:
var string = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
// Take all the roots
var roots = string.split(',');
// We will attach it to every node and keep it incrementing
var key = 1;
// The final result will be in this object
var result = [];
// Loop through to found roots
roots.forEach(function(root) {
// Take all the children
var items = root.split('//');
var parent = result;
// Loop through the available children
items.forEach(function(item, i) {
// Find if the current item exists in the tree
var child = getChild(parent, item);
if (!child) {
child = {
title: item,
key: key++
}
// This will ensure that the current node is a folder only
// if there are more children
if (i < items.length - 1) {
child.folder = true;
child.children = [];
}
// Attach this node to parent
parent.push(child);
}
parent = child.children;
});
});
console.log(result);
// Utility function to find a node in a collection of nodes by title
function getChild(parent, title) {
for (var i = 0; i < parent.length; i++) {
if (parent[i].title === title) {
return parent[i];
}
}
}
This is the draft code which came in my mind at first. I believe it can be improved further in terms of complexity.
var key = 1; // keys start at 1
let addPaths = (root, paths) => {
if (!paths || paths.length == 0)
return;
let path = paths.shift();
//add nodes for the current path
addNodes(root, path.split('//'));
// keep going until all paths have been processed
addPaths(root, paths);
};
let addNodes = (root, nodeList) => {
if (!nodeList || nodeList.length == 0)
return;
let title = nodeList.shift();
// find node under root with matching title
let isRootNode = Array.isArray(root);
node = (isRootNode ? root : root.children || []).find((node) => {
return node.title == title;
});
if (!node){
node = {
title: title,
key: key++
}
// are we at root of object?
if (isRootNode)
root.push(node);
else
{
if (!root.children)
root.children = [];
root.children.push(node);
root.folder = true;
}
}
addNodes(node, nodeList);
};
let parse = (string) => {
let object = [];
let nodes = string.split(',');
addPaths(object, nodes);
return object
};
console.log(JSON.stringify(parse("A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2"), null, 2));
Which results in:
[
{
"title": "A",
"key": 1,
"children": [
{
"title": "a1",
"key": 2
},
{
"title": "a2",
"key": 3
},
{
"title": "a3",
"key": 4,
"children": [
{
"title": "a31",
"key": 5
},
{
"title": "a32",
"key": 6
}
],
"folder": true
}
],
"folder": true
},
{
"title": "B",
"key": 7
},
{
"title": "C",
"key": 8,
"children": [
{
"title": "c1",
"key": 9
},
{
"title": "c2",
"key": 10
}
],
"folder": true
}
]
Try below code. I have used associative array to store already processed folder for faster lookup.
I hope it helps you.
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
var sArr = sString.split(","); // We will split it by comma so that we can iterate through its items.
var output = []; // Final result will be stored here.
var hash = {}; // It used to keep track of itemObjectect's position for faster lookup.
var counter = 1; // Its value will be used to assign to key;
for(var i = 0; i < sArr.length; i++){
var items = sArr[i].split("//");
var itemObject = {}; // Object to store value of each item.
var parentItemObject = {}; // It will refer to current parentObject during iteration.
for(var j = 0; j < items.length; j++){
// Check if item is already processed and stored in hash map.
if(hash.hasOwnProperty(items[j])){
// Check if parent Object value is empty then we will fetch it from hash directly.
if(isEmpty(parentItemObject)){
parentItemObject = output[hash[items[j]]];
}
else{
// It is parent element but is child of another element. Then we will fetch it from it's children array.
if(typeof parentItemObject.children !== "undefined"){
parentItemObject = parentItemObject.children[hash[items[j]]];
}
}
continue;
}
itemObject.title = items[j];
itemObject.key = counter++;
// Check if it is a folder item.
if(j != items.length -1){
itemObject.folder = true;
itemObject.children = [];
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
else{
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
parentItemObject = itemObject;
}
}
else{
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
}
itemObject = {};
}
//console.log(items);
}
function isEmpty(itemObject) {
return Object.keys(itemObject).length === 0;
}
//console.log(hash);
console.log(JSON.stringify(output,null,2));

Javascript: filter array of objects by array of strings

I wonder if there is a more elegant way of doing this. Suppose i have an array of objects like this:
a = [
{
"id": "kpi02",
"value": 10
},
{
"id": "kpi02",
"value": 30
},
{
"id": "kpi02",
"value": 11
},
{
"id": "kpi02",
"value": 33
},
{
"id": "kpi03",
"value": 1
},
{
"id": "kpi03",
"value": 0.5
},
{
"id": "kpi04",
"value": 0.5
}
]
Now i want to filter on the id property, to return all objects with a match in another array
var kpis = ["kpi03", "kpi02"];
I came up with this solution:
var b = [];
for (j in kpis) {
for (i in a) {
if (a[i].id == kpis[j]) {
b.push(a[i]);
}
}
}
Coming from R, this seems a bit complicated, is there any way to do that with the filter prototype? Like this but with an array of strings to compare with instead of a single string:
var b = a.filter( function(item){return (item.id == "kpi03");} );
Thanks a lot!
You can use indexOf in filter, like this
var res = a.filter(function (el) {
return kpis.indexOf(el.id) >= 0;
});
Example
Another nice alternative is using .filter with .includes:
var result = a.filter(item => kpis.includes(item.id))
Just make use of Array.indexOf
var b = a.filter(function(item){return kpids.indexOf(item.id) > -1 });
Array.indexOf returns the index of the argument passed in the array on which indexOf is being called on. It returns -1 if there isn't the element which we are looking for.
So, we make sure that it index is greater than -1
If you want them in the order of the array of string then
var result = [];
kpis.map((item) => {
const matchedObject = a.find(
(option) => option.id === item
);
result.push(matchedObject);
});

Building Array in order of income objects

I'm getting an XML and parsing it, saving it to array, the problems is that I get objects in this order:
temp1.ID = 15
temp1.name = "Dan"
temp1.phone = "32332"
temp2.ID = 12
temp2.name = "Test"
temp2.phone = 53463
temp3.ID = 2
temp3.name = "Tom"
temp3.phone = 12443
.
.
.
.
Object - its an objects that I get inside a loop while parsing XML
What I try is to save them in the same order I started to read them : Array: [temp1,temp2,temp3]
But The result of the next function is : Array: [temp3,temp2,temp1]
the function:
this.mytempect = [];
for (var i = 0; i < xml.length; i++) {
var temp = {};
temp.ID = parseXmlByTag(xml[i], "ID");
temp.name = parseXmlByTag(xml[i], "name");
temp.phone = parseXmlByTag(xml[i], "phone");
if (this.mytempect [temp .ID] == null) {
this.mytempect [temp .ID] = [];
}
this.mytempect [temp .ID].push(obj);
}
Before I save each object I check if I need to create for him a new Key or to add to existing one, in the end I get something like this:
I need to save the order in which I'm getting them so I'll save them in the order I entered them
If I understand your question here's what I think you should be doing. You seem to be confusing objects and arrays: mytempect needs to be an object if you want to store arrays against a key set by the ID.
Following your example, objects with the same key are assigned to the same array (identified by that key in the object) in the order in which they are read.
// create an object, not an array
this.mytempect = {};
for (var i = 0; i < arr.length; i++) {
var temp = {};
temp.ID = arr[i].ID;
temp.name = arr[i].name;
temp.phone = arr[i].phone;
// Don't check for null here because `this.mytempect[temp.ID]` might not exist
if (!this.mytempect[temp.ID]) {
this.mytempect[temp.ID] = [];
}
this.mytempect[temp.ID].push(temp);
}
DEMO
The demo produces an object with one object in an array under key 15, two under 12 and one under 2:
{
"2": [
{
"ID": 2,
"name": "Tom",
"phone": 12443
}
],
"12": [
{
"ID": 12,
"name": "Test",
"phone": 53463
},
{
"ID": 12,
"name": "Test",
"phone": 53462
}
],
"15": [
{
"ID": 15,
"name": "Dan",
"phone": "32332"
}
]
}
Note: you can't order the object in any way.
Perhaps you're looking for something like this
var mytempect = [],
dict = {},
i,
tmp;
for (i = 0; i < xml.length; ++i) {
tmp = {
ID: parseXmlByTag(xml[i], "ID"),
name: parseXmlByTag(xml[i], "name"),
phone: parseXmlByTag(xml[i], "phone")
};
if (!(tmp.ID in dict)) {
mytempect.push(dict[tmp.ID] = []);
}
dict[tmp.ID].push(tmp); // use fact Objects ByRef to add item
}
dict = null; // cleanup
The Array mytempect will now have indices 0, 1, 2, etc containing Arrays of all Objects which have the same ID. With your sample data you will get
mytempect[0][0].ID === 15;
mytempect[1][0].ID === 12;
mytempect[2][0].ID === 2;

Remove duplicate objects from an array using javascript

I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}

Count duplicates within an Array of Objects

I have an array of objects as follows within my server side JS:
[
{
"Company": "IBM"
},
{
"Person": "ACORD LOMA"
},
{
"Company": "IBM"
},
{
"Company": "MSFT"
},
{
"Place": "New York"
}
]
I need to iterate through this structure, detect any duplicates and then create a count of a duplicate is found along side each value.
Both of the values must match to qualify as a duplicate e.g. "Company": "IBM" is not a match for "Company": "MSFT".
I have the options of changing the inbound array of objects if needed. I would like the output to be an object, but am really struggling to get this to work.
EDIT: Here is the code I have so far where processArray is the array as listed above.
var returnObj = {};
for(var x=0; x < processArray.length; x++){
//Check if we already have the array item as a key in the return obj
returnObj[processArray[x]] = returnObj[processArray[x]] || processArray[x].toString();
// Setup the count field
returnObj[processArray[x]].count = returnObj[processArray[x]].count || 1;
// Increment the count
returnObj[processArray[x]].count = returnObj[processArray[x]].count + 1;
}
console.log('====================' + JSON.stringify(returnObj));
For example:
counter = {}
yourArray.forEach(function(obj) {
var key = JSON.stringify(obj)
counter[key] = (counter[key] || 0) + 1
})
Docs: Array.forEach, JSON.stringify.
Object.prototype.equals = function(o){
for(var key in o)
if(o.hasOwnProperty(key) && this.hasOwnProperty(key))
if(this[key] != o[key])
return false;
return true;
}
var array = [/*initial array*/],
newArray = [],
ok = true;
for(var i=0,l=array.length-1;i<l;i++)
for(var j=i;j<l+1;j++)
{
if(!array[i].equals(array[j]))
newArray.push(array[i]);
}
We are required to write a JavaScript function that takes in one such array of objects. The function creates and return a new array in which no objects are repeated (by repeated we mean objects having same value for "Country" property.)
Moreover, the function should assign a count property to each object that represents the number of times they appeared in the original array.
const arr = [
{
"Country": "BR",
"New Lv1−Lv2": "#N/A"
},
{
"Country": "BR",
"New Lv1−Lv2": "#N/A"
},
{
"Country": "",
"New Lv1−Lv2": "test"
}];
const convert = (arr) => {
const res = {};
arr.forEach((obj) => {
const key = `${obj.Country}${obj["New Lv1−Lv2"]}`;
if (!res[key]) {
res[key] = { ...obj, count: 0 };
};
res[key].count += 1;
});
return Object.values(res);
};
console.log(convert(arr));
know more
With ES6, one can use Array#reduce with an object to store the counts.
let counts = arr.reduce((acc, curr)=>{
const str = JSON.stringify(curr);
acc[str] = (acc[str] || 0) + 1;
return acc;
}, {});
Demo
To create a new array without duplicates, a Set can be used with Array#filter.
let set = new Set;
let res = arr.filter(x => {
const str = JSON.stringify(x);
return !set.has(str) && set.add(str);
});
Demo

Categories