Need an algorithm to manipulate array structure in javascript - javascript

In javascript, here is my start array:
[{
name: 'aaa',
value: 1
},
{
name: 'bbb',
value: 0
},
{
name: 'bbb',
value: 1
}]
I want to transform it into this array as result:
[{
name: 'aaa',
value: 1
},
{
name: 'bbb',
value: [0, 1]
}]
I need a good and simple algorithm to do this

How about:
var array = [{
name: 'aaa',
value: 1
},
{
name: 'bbb',
value: 0
},
{
name: 'bbb',
value: 1
}];
var map = {};
for(var i = 0; i < array.length; i++) {
var name = array[i].name;
if (map[name] === undefined) {
map[name] = [];
}
map[name].push(array[i].value);
}
var result = [];
for(var key in map) {
var value = map[key];
result.push({
name: key,
value: value.length === 1 ? value[0] : value
});
}
Easiest way is to create a map to keep track of which names are used. Then convert this map back to an array of objects.
If you want to use Arrays for value then change it to:
result.push({
name: key,
value: value
});

here's pseudocode for simplest implementation
hash = {}
for(pair in array) {
hash[pair.name] ||= []
hash[pair.name] << pair.value
}
result = []
for(k, v in hash) {
result << {name: k, value: v}
}

This function does the trick
function consolidate(var arrayOfObjects)
{
// create a dictionary of values first
var dict = {};
for(var i = 0; i < arrayOfObjects.length; i++)
{
var n = arrayOfObjects[i].name;
if (!dict[n])
{
dict[n] = [];
}
dict[n].push(arrayOfObjects[i].value);
}
// convert dictionary to array again
var result = [];
for(var key in dict)
{
result.push({
name: key,
value: dict[key].length == 1 ? dict[key][0] : dict[key]
});
}
return result;
}

An alternative solution:
function convert(arr) {
var res = [];
var map = {};
for (var i=0;i<arr.length;i++) {
var arrObj = arr[i];
var oldObj = map[arrObj.name];
if (oldObj == undefined) {
oldObj = {name:arrObj.name, value:arrObj.value};
map[arrObj.name] = oldObj;
res.push(oldObj);
} else {
if( typeof oldObj.value === 'number' ) {
oldObj.value = [oldObj.value];
}
oldObj.value.push(arrObj.value);
}
}
return res;
}
In theory it should work a bit faster and use less memory. Basically it creates a result array and a map which is an index for the same array (no duplicate objects). So it fills the result in one iteration instead of two and does not need to convert map to array (which saves several CPU cycles :P ).
Added:
Here is a variation of that function in case value: [1] is acceptable:
function convert(arr) {
var res = [];
var map = {};
for (var i=0;i<arr.length;i++) {
var arrObj = arr[i];
var oldObj = map[arrObj.name];
if (oldObj == undefined) {
oldObj = {name:arrObj.name, value:[arrObj.value]};
map[arrObj.name] = oldObj;
res.push(oldObj);
} else {
oldObj.value.push(arrObj.value);
}
}
return res;
}

Related

How can i get key of nested object = used For....in?

Here is my code. How can i get the key of the key-value pair using for loop?
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
The desired output is as follows:
/* desired results :
* bedroom
* area
* bed
* type
* price
*/
Please help
var getKeys = function(obj) {
var keys = [];
Object.keys(obj).forEach(function(key){
keys.push(key);
if(typeof obj[key] == 'object'){
keys = keys.concat(getKeys(obj[key]));
}
})
return keys;
}
Then
var keys = getKeys(apartment);
You can use a simple Regex as follow:
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
let result = [];
let jsonstr = JSON.stringify(apartment);
// {"bedroom":{"area":20,"bed":{"type":"twin-bed","price":100}}}
let regex = /"(\w+)":/g;
jsonstr.replace(regex, function(match,prop){
result.push(prop);
});
console.log(result);
we can easily done by using regex, convert object string and apply regex to extract the particular word
run the snippet for required output
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
apartment = JSON.stringify(apartment);
var re = /(")\w+(")(:)/g;
var match;
do {
match = re.exec(apartment);
if (match) {
console.log(match[0]);
}
} while (match);
regex : /(")\w+(")(:)/g
only extracts key for more click here
do while loop responsible to detect multiple match in the string
You can use a recursive function :
function getKeys(source, dest) {
for (let key in source) {
if (typeof source[key] == 'object') {
dest.push(key)
getKeys(source[key], dest)
} else {
dest.push(key)
}
}
return dest
}
result = []
const apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
}
getKeys(apartment, result) // ["bedroom", "area", "bed", "type", "price"]
var inputs = [
{a:1,b:2,c:3}, // Simple object
{a:{b:2,c:3}}, // Simple object with nesting
{a:{a:{b:2,c:3}}}, // Repeated key hiding nesting
{a:[{b:2,c:3}]}, // keys behind array
];
inputs.push(inputs); // reference cycle and array at top
function getKeys(obj) {
var all = {};
var seen = [];
checkValue(obj);
return Object.keys(all);
function checkValue(value) {
if (Array.isArray(value)) return checkArray(value);
if (value instanceof Object) return checkObject(value);
}
function checkArray(array) {
if (seen.indexOf(array) >= 0) return;
seen.push(array);
for (var i = 0, l = array.length; i < l; i++) {
checkValue(array[i]);
}
}
function checkObject(obj) {
if (seen.indexOf(obj) >= 0) return;
seen.push(obj);
var keys = Object.keys(obj);
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
all[key] = true;
checkValue(obj[key]);
}
}
}
var result = inputs.map(getKeys);
console.log(result);

Filter Multidimensional Array

I have my dataset configured as
var x = [
{"phaseName":"Initiation","phaseID":"595e382f1a1e9124d4e2600c"},
{"phaseName":"Execution","phaseID":"595e38321a1e9124d4e2600d"}
]
I want to write some function which give the output of filtered data.
For example
var y ="Initiation"
samplefunction(y)
and i get the whole line of {"phaseName":"Initiation","phaseID":"595e382f1a1e9124d4e2600c"}
Use Array#filter
var x = [{
"phaseName": "Initiation",
"phaseID": "595e382f1a1e9124d4e2600c"
},
{
"phaseName": "Execution",
"phaseID": "595e38321a1e9124d4e2600d"
}
];
function filter(phaseName) {
return x.filter(item => {
return item.phaseName === phaseName;
});
}
console.log(filter('Initiation'));
You can also use a simple for loop:
var x = [{
"phaseName": "Initiation",
"phaseID": "595e382f1a1e9124d4e2600c"
},
{
"phaseName": "Execution",
"phaseID": "595e38321a1e9124d4e2600d"
}
];
var y = "Initiation";
function samplefunction(value) {
var newArr = new Array(); //creating a new array to store the values
for (var i = 0; i < Object.keys(x).length; i++) { //looping through the original array
if (x[i].phaseName == value) {//check if the phase name coresponds with the argument
newArr.push(x[i]); //push the coressponding value to the new array
}
}
return newArr; //return the new array with filtered values
}
var result = samplefunction(y);
console.log(result);
You could test all properties for the wanted value and filter the array.
function filter(array, value) {
return array.filter(function (object) {
return Object.keys(object).some(function (key) {
return object[key] === value;
});
});
}
var data = [{ phaseName: "Initiation", phaseID: "595e382f1a1e9124d4e2600c" }, { phaseName: "Execution", phaseID: "595e38321a1e9124d4e2600d" }];
console.log(filter(data, "Initiation"));

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

How to check if array is unique on specific object property?

I have an array of objects:
var array1 = [
{
property1: 10,
property2: "abc"
},
{
property1: 11,
property2: "def"
},
{
property1: 10,
property2: "ghi"
}
];
Now what I want is this array will be said not unique as per value of property1.
This means that this array contains 2 elements with property1=10, so the array does not contain unique value of property1.
To check this, I can use a for loop:
for (var i = 0; i < array1.length; i++) {
var array2 = array1.slice(); // copy array
array2.remove(array1[i]);
var temppropety1 = array1[i].property1;
for (var j = 0; j < array2.length; j++) {
if (array2[J].property1==temppropety1) {
return true;
}
}
}
But is there an easier way or a library to find this?
Here is a straightforward way to test for uniqueness on property1. Loop through the objects in the outer array and add each object's property1 to a temp array if it is not already in that temp array. If a duplicate value is encountered, return false meaning property1 is not unique.
function isUnique(arr) {
var tmpArr = [];
for(var obj in arr) {
if(tmpArr.indexOf(arr[obj].property1) < 0){
tmpArr.push(arr[obj].property1);
} else {
return false; // Duplicate value for property1 found
}
}
return true; // No duplicate values found for property1
}
Demo: http://jsbin.com/lohiqihipe/1/
First, you could reduce (aggregate) the objects by grouping them by the value of property1:
var grouped = array.reduce(function(grouped, item) {
var propertyValue = item.property1;
grouped[propertyValue] = (grouped[propertyValue] || 0) + 1;
return grouped;
}, {});
Then you check that every key of the resulting object has a value of 1:
var result = Object.keys(grouped).every(function(key) {
return grouped[key] === 1;
});
I suggest that array can be quite big so I'd prefer not to copy it and just validate properties.
Also it is not an option to use map function of array because in this case you won't be able to break a cycle on first match:
var equals = function(array) {
var co = {};
var unique = true;
for(var i = 0; i < array.length; i++) {
var o = array[i];
if (co[o.property1]) {
unique = false;
break;
} else {
co[o.property1] = true;
}
}
return unique;
};
You can convert your array to flat structure:
array1.map(function(item) { return item.property1; });
and now your problem simplify to check duplicates in simple array
var array1 = ["a","b","b","c","d","e","f"];
var uniqueItems = [];
$.each(array1, function(i, el){
if($.inArray(el, uniqueItems) === -1) uniqueItems.push(el);
});
References:
https://stackoverflow.com/a/840808/4772988
https://stackoverflow.com/a/9229932/4772988
You can use a couple of helpers to abstract it:
var uniqBy = function(f, xs) {
var seen = []
return xs.filter(function(x) {
var fx = f(x)
if (seen.indexOf(fx) > -1) return
seen.push(fx)
return true
})
}
var dot = function(k) {
return function(obj) {
return obj[k]
}
}
Then filter out duplicates by the property, and compare the length of the result to the original array. If they don't match, then they must not be unique:
var res = uniqBy(dot('property1'), array1)
var isUnique = array1.length === res.length
console.log(isUnique) // false
If you got only numbers or only strings to remove duplicates from, then you can improve performance by using an object instead of an array to keep track of elements seen so far.
You can use lodash library to achieve this.
Here is the library documentation: https://lodash.com/docs/4.17.5#filter
Method:-
function isDuplicatesPresent(list, propertyName){
return _.filter(list, function (value) {
return _.filter(list, function(innerValue){ reutrn innerValue[propertyName] === value[propertyName]}).length > 1;
}).length > 0;
}
Example:-
var users = [
{ user: 'barney', age: 36, active: true },
{ user: 'fred', age: 40, active: false },
{ user: 'barney', age: 37, active: true}
];
let duplicates = _.filter(users, function (value) {
return _.filter(users, {user:value.user}).length > 1;
});
Result:
console.log(duplicates)
> [
{"user": "barney","age": 36,"active": true},
{"user": "barney","age": 37,"active": true}
];

Getting index of array object element given multiple object keys

I know given a single key (for example, if I know the object.name = 'Sam') using:
var index = array.map(function(el) {return el.name}).indexOf('Sam');
I can get the index of the array element with object.name = 'Sam'
However say I have several elements with object.name ='Sam' in the array, but now I know know the object.name, object.age and object.size - is it possible to adapt the above code to get the index but also checking against object.age and object.size?
Assuming you have the values in variables such as name, age and size as you mentioned in comments, You can use a function like:
function findInArray(arr) {
for (var i = 0; i < arr.length; i++) {
var el = arr[i];
if (el.name == name && el.age == age && el.size == size)
return i;
}
return -1;
};
Which will return the index of object in array if match is found, and -1 otherwise...
var data = [{
name: "Sis",
age: "17",
size: "10"
}, {
name: "Sam",
age: "17",
size: "10"
}, {
name: "Som",
age: "17",
size: "10"
}],
name = "Sam",
age = "17",
size = "10";
function findInArray(arr) {
for (var i = 0; i < arr.length; i++) {
var el = arr[i];
if (el.name == name && el.age == age && el.size == size)
return i;
}
return -1;
};
console.log(findInArray(data));
If you're using the awesome underscore library there's a _.findWhere function.
var sam21 = _.findWhere(people, {
name: 'Sam',
age: 21
});
if you want something without a whole other library you can use .filter.
var sam21 = people.filter(function(person) {
return person.age === 21 && person.name === 'Sam';
});
I just noticed you're looking for the index. This answer can be useful: https://stackoverflow.com/a/12356923/191226
You could use a function like this one
indexOfObjectArray = function(array, keyvalues) {
for (var i = 0; i < array.length; i++) {
var trueCount = 0;
for (var j = 0; j < keyvalues.length; j++) {
if (array[i][keyvalues[j]['key']] == keyvalues[j]['value']) {
trueCount++;
}
}
if (trueCount === keyvalues.length) return i;
}
return -1; }
and use it like that for example:
var yourArray = [{id: 10, group: 20},...];
var keyvalues = [
{ 'key': 'id', 'value': 10 },
{ 'key': 'group', 'value': 20 }];
var index = indexOfObjectArray(yourArray, keyvalues );
This function will return the index of an object that has id = 10 and group = 20

Categories

Resources