Getting only values from object literal - javascript

Going on from this question: Getting first object from javascript litteral
Would it be possible to get only the values from a group of name:value pairs? So lets say we have a list of objects, each object having any number of values:
var items = [
{name:"Foo", age:16, gender:"m"},
{name:"Bar", age:17, gender:"f"},
{name:"foo", age:16, gender:"m"},
{name:"bar", age:18, gender:"m"},
{name:"foobar", age:18, gender:"f"},
{name:"barfoo", age:20, gender:"f"}
];
How can I return a list like:
var items = [
["Foo", 16, "m"],
["Bar", 17, "f"],
["foo", 16, "m"],
["bar", 18, "m"],
["foobar", 18, "f"],
["barfoo", 20, "f"]
];
I have tried this but was wondering if there was a better way of doing it.
Array.prototype.getValues = function () {
if(typeof(this[0]) != typeof({}))
throw "Array values are expected to be == typeof({})";
var items = [];
for (var i = 0; i < this.length; i++) {
var r = [];
for (var l in this[i]) {
r.push(this[i][l]);
}
items.push(r);
}
return items;
};

var newitems=[];
//loop through each item in the original
for(var i =0; i<items.length; i++)
{
newitems[i] = [];
//loop over the properties in each element and push it to an array
for(var prop in items[i]){
newitems.push(items[i][prop]);
}
}
or alternatively:
var newitems = items.map(function(elem){
var item = [];
for(var prop in elem){
item.push(elem[prop]);
}
return item;
})

Related

JavaScript - Check every element of 2D arrays for match

I am trying to compare an array of elements with the elements of a 2D array. If there is a match found, then the count for that row of 2D elements will increase. I managed to do it with the first row of 2D array however I do not know how to make the code keep checking for the next row of the 2D array.
var fruits=[
['apple', 'banana', 'mango'],
['grape', 'pineapple', 'blueberry'],
['strawberry', 'mangosteen']
];
var fruit_i_like=[
['grape', 'banana', 'pineapple']
];
//if found match from the first row of fruits, increment this var
var fruit1_count = 0;
//if found match from the second row of fruits, increment this var
var fruit2_count = 0;
//if found match from the third row of fruits, increment this var
var fruit3_count = 0;
for (var i = 0; i < fruit_i_like.length; i++) {
for (var j = 0; j < fruits.length; j++){
if (fruits[j].indexOf(fruit_i_like[i]) > -1) {
fruit1_count++;
}
}
}
The expected result should be printing the number of match the fruit_i_like array has with every rows of the array fruits. For example, here fruit1_count would be 1, fruit2_count would be 2, fruit3_count would be 0.
Is there any way of checking the other rows, using pure JS? Thank you!
Probably better to use an array than variables in this case, so you don't have to hardcode names and definitions. In this case, you'll get an ordered list of each fruit's count at the end from the array, in the order you initially put them in the fruits array (not sure why fruit_i_like is two-dimensional, either).
var fruits = [
['apple', 'banana', 'mango'],
['grape', 'pineapple', 'blueberry'],
['strawberry', 'mangosteen']
];
var fruit_i_like = [
['grape', 'banana', 'pineapple']
];
let fruitCounts = [];
fruits.forEach((fruitsList, i) => {
fruitCounts.push(0);
fruit_i_like[0].forEach(likedFruit => {
if (fruitsList.includes(likedFruit)) {
fruitCounts[i]++;
}
});
});
fruitCounts.forEach((count, i) => {
console.log(`Fruit count ${i} = ${count}`);
});
1) You can easily achieve the result using Set and simple for loop. You can first create an object of properties in which count you want as:
const obj = {
fruit1_count: 0,
fruit2_count: 0,
fruit3_count: 0,
};
var fruits = [
["apple", "banana", "mango"],
["grape", "pineapple", "blueberry"],
["strawberry", "mangosteen"],
];
var fruit_i_like = [["grape", "banana", "pineapple"]];
const obj = {
fruit1_count: 0,
fruit2_count: 0,
fruit3_count: 0,
};
const set = new Set(fruit_i_like[0]);
for (let i = 0; i < fruits.length; ++i) {
const arr = fruits[i];
for (let j = 0; j < arr.length; ++j) {
if (set.has(arr[j])) obj[`fruit${i + 1}_count`]++;
}
}
const { fruit1_count, fruit2_count, fruit3_count } = obj;
console.log(fruit1_count);
console.log(fruit2_count);
console.log(fruit3_count);
2) You can also use reduce and forEach here as:
var fruits = [
["apple", "banana", "mango"],
["grape", "pineapple", "blueberry"],
["strawberry", "mangosteen"],
];
var fruit_i_like = [["grape", "banana", "pineapple"]];
const obj = {
fruit1_count: 0,
fruit2_count: 0,
fruit3_count: 0,
};
const set = new Set(fruit_i_like[0]);
const resultObj = fruits.reduce((acc, curr, i) => {
curr.forEach((o) => {
if (set.has(o)) acc[`fruit${i + 1}_count`]++;
});
return acc;
}, obj);
const { fruit1_count, fruit2_count, fruit3_count } = resultObj;
console.log(fruit1_count);
console.log(fruit2_count);
console.log(fruit3_count);
I'm a little late to the party, but you can also do this with a pair of nested Array.reduce() methods, and using Array.includes() in the nested reduce to increment the count if included in the array.
const fruits = [
['apple', 'banana', 'mango'],
['grape', 'pineapple', 'blueberry'],
['strawberry', 'mangosteen']
];
const fruit_i_like = [
['grape', 'banana', 'pineapple']
];
const [fruit1_count, fruit2_count, fruit3_count] = fruits.reduce((a, c, i) => {
a[i] = fruit_i_like[0].reduce((aa, cc) => aa + c.includes(cc), 0);
return a;
}, new Array(fruits.length));
console.log({
fruit1_count: fruit1_count,
fruit2_count: fruit2_count,
fruit3_count: fruit3_count
})

Join same properties value from Objects

I have an array of object, I want to know the best way of concatenating values from similar properties e.g.
arr:[
{obj:{obj_type:1, obj_foo:"joe"}},
{obj:{obj_type:2, obj_foo:"developer"}},
{obj:{obj_type:1, obj_foo:"kevin"}},
{obj:{obj_type:2, obj_foo:"architect"}}
]
I need to concatenate properties value of same obj_type property.
expected result should be:
arr:[
{obj:{obj_type:1, obj_foo:"joe|kevin"}},
{obj:{obj_type:2, obj_foo:"developer|architect"}}
]
i.e. values are concatenated based on obj_type.
I think code like this might be helpful for you:
//Objects to work with:
var arr = [{obj:{obj_type:1, obj_foo:"joe"}},
{obj:{obj_type:2, obj_foo:"developer"}},
{obj:{obj_type:1, obj_foo:"kevin"}},
{obj:{obj_type:2, obj_foo:"architect"}}];
//Map from obj_type to {obj: …} objects:
var map = {};
//Iterating arr:
for(var i = 0; i < arr.length; i++){
var o = arr[i], type = o.obj.obj_type;
if(type in map){
map[type].obj.obj_foo += '|' + o.obj.obj_foo;
}else{
map[type] = o;
}
}
//Putting map values to arr:
arr = [];
for(var key in map){
arr.push(map[key]);
}
//Done:
console.log(arr);
Produced output looks like this:
[ { obj: { obj_type: 1, obj_foo: 'joe|kevin' } },
{ obj: { obj_type: 2, obj_foo: 'developer|architect' } } ]
This variant doesn't change content of initial array.
var types = {};
var newArr = [];
var type, newObj;
for ( var i = 0; i < arr.length; ++i ) {
type = arr [ i ].obj.obj_type;
if ( type in types ) {
types[ type ].obj.obj_foo += '|' + arr[ i ].obj.obj_foo;
} else {
newObj = {
obj: {
obj_type: arr[ i ].obj.obj_type,
obj_foo: arr[ i ].obj.obj_foo
}
};
types[ type ] = newObj;
newArr.push( newObj );
}
}
return newArr; // result array
This might be the simplest approach:
// Your array
var arr = [
{obj:{obj_type:1, obj_foo:"joe"}},
{obj:{obj_type:2, obj_foo:"developer"}},
{obj:{obj_type:1, obj_foo:"kevin"}},
{obj:{obj_type:2, obj_foo:"architect"}}
];
// Loop over all elements
for(var i = 0; i < arr.length; i++) {
var a = arr[i].obj;
// Compare to each other element
for(var j = i + 1; j < arr.length; j++) {
var b = arr[j].obj;
// If the obj_type is equal...
if(a.obj_type === b.obj_type) {
// Merge data...
a.obj_foo += '|' + b.obj_foo;
// Remove other element
arr.splice(j--, 1);
}
}
}
Output (from node.js):
[ { obj: { obj_type: 1, obj_foo: 'joe|kevin' } },
{ obj: { obj_type: 2, obj_foo: 'developer|architect' } } ]

Remove duplicates from array of arrays by the first object

I have an array of arrays which looks like this:
arr = [
["Bob","USA","55"],
["Frank","Canada","20"],
["Bob","UK","35"],
["Bob","France","38"],
["Anna","Poland","22"]
]
I like to remove duplicate arrays which have the same value on the first position(the same name) - so I'd like to my output will look like that:
arr = [
["Bob","USA","55"],
["Frank","Canada","20"],
["Anna","Poland","22"]
]
I'm trying to do this in this way:
uniqueArr = []
for (var i in arr) {
if (uniqueArr.indexOf(arr[i][0]) === -1)) {
uniqueArr.push(arr[i][0])
}
Everything works ok - my output looks like Bob, Frank, Anna
But the problem is when I'm trying to recive whole arrays with unique value name. When I'm doing:
uniqueArr = []
for (var i in arr) {
if (uniqueArr.indexOf(arr[i][0]) === -1)) {
uniqueArr.push(arr[i])
}
My output looks exactly like the input array. Do you know where I'm doing wrong?
You could keep track of the key string in a separate array and track that instead, for example:
var uniqueArr = [],
keys = []; // Create an array for storing the key values
for (var i in arr) {
if (keys.indexOf(arr[i][0]) === -1) {
uniqueArr.push(arr[i]); // Push the value onto the unique array
keys.push(arr[i][0]); // Push the key onto the 'key' array
}
}
console.log(uniqueArr);
jsFiddle example
try
arr = [
["Bob", "USA", "55"],
["Frank", "Canada", "20"],
["Bob", "UK", "35"],
["Bob", "France", "38"],
["Anna", "Poland", "22"]
]
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (isInArr(newArr, arr[i]) == -1) {
newArr.push(arr[i]);
}
}
function isInArr(checkArr, value) {
var index = -1;
for (var i = 0; i < checkArr.length; i++) {
if (checkArr[i][0] == value[0]) {
index = i;
break;
}
}
return index;
}
console.log(newArr)
DEMO
Take a look atthe function unique2. It works!
originalArr = [
["Bob", "USA", "55"],
["Frank", "Canada", "20"],
["Bob", "UK", "35"],
["Bob", "France", "38"],
["Anna", "Poland", "22"]
];
function unique(arr) {
uniqueArr = [];
for (var i in arr) {
if (uniqueArr.indexOf(arr[i][0]) === -1) {
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
}
function unique2(arr) {
uniqueArr = [];
keys = [];
for (var i in arr) {
if (keys.indexOf(arr[i][0]) === -1) {
uniqueArr.push(arr[i]);
keys.push(arr[i][0]);
}
}
return uniqueArr;
}
var response = document.getElementById('response');
response.textContent = JSON.stringify(unique(originalArr));
var response2 = document.getElementById('response2');
response2.textContent = JSON.stringify(unique2(originalArr));
<h1>Your code</h1>
<div id="response"></div>
<h1>My code</h1>
<div id="response2"></div>

How to merge JSON objects using JavaScript?

How to merge JSON objects using plain(without jQuery) JavaScript?
Requirement is to:
Convert from:
chartData=[
{"date":"2014-05-1","CAT1":0.1},
{"date":"2014-05-1","CAT2":0.2},
{"date":"2014-05-1","CAT3":0.3},
{"date":"2014-05-1","UNSET":0.4},
{"date":"2014-05-2","CAT1":0.4},
{"date":"2014-05-2","CAT2":0.3},
{"date":"2014-05-2","CAT3":0.2},
{"date":"2014-05-2","UNSET":0.1}
];
Convert To:
chartData=[
{"date":"2014-05-1","CAT1":0.1,"CAT2":0.2,"CAT3":0.3,"UNSET":0.4},
{"date":"2014-05-2","CAT1":0.4,"CAT2":0.3,"CAT3":0.2,"UNSET":0.1}
]
Here's an example of how to do this... no jquery required.
chartData=[{"date":"2014-05-1","CAT1":0.1},{"date":"2014-05-1","CAT2":0.2},{"date":"2014-05-1","CAT3":0.3},{"date":"2014-05-1","UNSET":0.4},{"date":"2014-05-2","CAT1":0.4},{"date":"2014-05-2","CAT2":0.3},{"date":"2014-05-2","CAT3":0.2},{"date":"2014-05-2","UNSET":0.1}];
function groupProps(orig, key) {
var newArr = [],
groups = {},
newItem, i, j, cur;
for (i = 0, j = orig.length; i < j; i++) {
cur = orig[i];
if (!(cur[key] in groups)) {
groups[cur[key]] = {date: cur[key] };
newArr.push(groups[cur[key]]);
}
for (var prop in cur) {
if (prop != key) {
groups[cur[key]][prop] = cur[prop];
}
}
}
return newArr;
}
console.log(groupProps(chartData, "date"))
Here we iterate backwards through the chartData array operating in place and splicing elements out of the array as the content is merged :
var chartData=[{"date":"2014-05-1","CAT1":0.1},{"date":"2014-05-1","CAT2":0.2},{"date":"2014-05-1","CAT3":0.3},{"date":"2014-05-1","UNSET":0.4}, {"date":"2014-05-2","CAT1":0.4},{"date":"2014-05-2","CAT2":0.3},{"date":"2014-05-2","CAT3":0.2},{"date":"2014-05-2","UNSET":0.1}];
var chartDates = {}; /* stores references to elements for each date */
for (var i=chartData.length-1; i >= 0; i--) {
var date = chartData[i]['date'];
if (date in chartDates) {
for (var k in chartData[i]) {
chartDates[date][k] = chartData[i][k];
}
chartData.splice(i,1);
} else {
chartDates[date] = chartData[i];
}
}
console.log(chartData);
Here is some code that will do it, we first loop through the array to group all of the non-date properties together by date. then append the date property to that intermediate result:
var chartData = [
{"date": "2014-05-1", "CAT1": 0.1},
{"date": "2014-05-1", "CAT2": 0.2},
{"date": "2014-05-1", "CAT3": 0.3},
{"date": "2014-05-1", "UNSET": 0.4},
{"date": "2014-05-2", "CAT1": 0.4},
{"date": "2014-05-2", "CAT2": 0.3},
{"date": "2014-05-2", "CAT3": 0.2},
{"date": "2014-05-2", "UNSET": 0.1}
];
function mergeValues(chartData) {
var tempObj = {};
for (i in chartData) {
var date = chartData[i].date;
//remove the date
delete chartData[i].date;
//get the remaining keys
var keys = Object.keys(chartData[i]);
tempObj[date] = tempObj[date] || {};
for (j in keys) {
tempObj[date][keys[j]] = chartData[i][keys[j]];
}
}
console.log(tempObj);
//{"2014-05-1":{ CAT1:0.1, CAT2:0.2, CAT3:0.3, UNSET:0.4}
//{"2014-05-2":{ CAT1:0.4, CAT2:0.3, CAT3:0.2, UNSET:0.1}
var arr = [];
var keys = Object.keys(tempObj);
for (k in keys) {
var obj = tempObj[keys[k]];
//add the date
obj.date = keys[k];
arr.push(obj);
}
return arr;
}
console.log(mergeValues(chartData));
//
//[
// {"CAT1":0.1,"CAT2":0.2,"CAT3":0.3,"UNSET":0.4,"date":"2014-05-1"},
// {"CAT1":0.4,"CAT2":0.3,"CAT3":0.2,"UNSET":0.1,"date":"2014-05-1"}
//]
Using underscore.js, it can be done pretty easily:
var groupedBy = _.groupBy(chartData, 'date');
var a = _.reduce(groupedBy, function(a, c) {
a.push(_.reduce(c, function(a2, c2){
for(var i in c2) { a2[i] = c2[i]; }
return a2;
}, { }));
return a;
},[]); // 'a' holds the desired merged object

How to merge arrays in javascript in a different way?

I want to merge arrays a little bit different way.
I have 2 or more arrays like:
var array1 = ["apple", "banana"];
var array2 = ["apple", "apple", "orange"];
I want the output:
var array3 = ["apple", "apple", "banana", "orange"];
So if any given array has a variable in it more than once, merge algorithm should keep all of them from that array.
I saw some code that prevents duplication but it gives outputs like this:
var array3 = ["apple", "banana", "orange"];
for more example:
var arr1 = [1,2,3,4];
var arr2 = [1,1,2,4,5,5,5];
var arr3 = [1,3,3,5,5];
I want the output:
var array4 = [1,1,2,3,3,4,5,5,5];
How can I do this?
Here's one way to do it by counting the occurrences of each item in each array:
var arr1 = [1,2,3,4];
var arr2 = [1,1,2,4,5,5,5];
var arr3 = [1,3,3,5,5];
function joinCommon(/* list of arrays */) {
var arr, arrayCounts, masterList = {}, item, output;
// for each array passed in
for (var i = 0; i < arguments.length; i++) {
arr = arguments[i];
arrayCounts = {};
// iterate each array
for (var j = 0; j < arr.length; j++) {
item = arr[j];
if (!arrayCounts[item]) {
arrayCounts[item] = 1;
} else {
++arrayCounts[item];
}
// now keep master list and master counts
if (!masterList[item]) {
masterList[item] = {cnt: 1, val: item};
} else {
masterList[item].cnt = Math.max(masterList[item].cnt, arrayCounts[item]);
}
}
}
// now output result
output = [];
for (var i in masterList) {
for (var j = 0; j < masterList[i].cnt; j++) {
output.push(masterList[i].val);
}
}
return output;
}
var results = joinCommon(arr1, arr2, arr3);
Working demo: http://jsfiddle.net/jfriend00/dtn6zw4m/
Here is a solution using ECMA5.
Javascript
function indexOf(items, value) {
return items.map(function (subitem) {
return subitem.value;
}).indexOf(value);
}
function countItems(previous, item) {
var atIndex = indexOf(previous, item);
if (atIndex !== -1) {
previous[atIndex].count += 1;
} else {
previous.push({
value: item,
count: 1
});
}
return previous;
}
function mergeCounts(item) {
var atIndex = indexOf(this, item.value);
if (atIndex === -1) {
this.push(item);
} else if (this[atIndex].count < item.count) {
this[atIndex] = item;
}
}
function expandCounts(previous, item) {
var iter;
for (iter = 0; iter < item.count; iter += 1) {
previous.push(item.value);
}
return previous;
}
function mergeArg(items, arg) {
arg.reduce(countItems, []).forEach(mergeCounts, items);
return items;
}
function mergeMaxItems() {
return [].reduce.call(arguments, mergeArg, []).reduce(expandCounts, []);
}
var arr1 = [1, 2, 3, 4],
arr2 = [1, 1, 2, 4, 5, 5, 5],
arr3 = [1, 3, 3, 5, 5];
document.body.appendChild(document.createTextNode(mergeMaxItems(arr1, arr2, arr3)));
I like to use ramda (http://ramdajs.com/docs/index.html) for this stuff
var arr1 = [1,2,3,4];
var arr2 = [1,1,2,4,5,5,5];
var arr3 = [1,3,3,5,5];
var allArrays = [arr1, arr2, arr3];
var allValues = R.compose(R.uniq, R.flatten)(allArrays);
var getItemCounts = R.countBy(function(item) {
return item;
});
var itemCounts = R.map(function(arr) {
return getItemCounts(arr);
})(allArrays);
var combined = [];
R.forEach(function(item) {
var countsForItem = R.pluck(item, itemCounts);
var maxCount = R.max(countsForItem);
combined.push.apply(combined, R.repeatN(item, maxCount));
})(allValues);
console.log(combined.sort());
JSFiddle: http://jsfiddle.net/pcr0q1xa/3/
Ramda is your friend.
function merge () {
return R.chain(R.apply(R.repeat), R.toPairs(R.reduce(
R.mergeWith(R.max),
{},
R.map(R.countBy(R.identity), arguments)
)))
}
var array1 = ["apple", "banana"];
var array2 = ["apple", "apple", "orange"];
console.log(JSON.stringify(merge(array1, array2)))
var arr1 = [1,2,3,4];
var arr2 = [1,1,2,4,5,5,5];
var arr3 = [1,3,3,5,5];
console.log(JSON.stringify(merge(arr1, arr2, arr3)))
<script src="http://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
Untested and not JS, but I think this is what you are looking for.
I have just tested it manually, it worked for your test cases.
while items in lista or items in listb
compare a.head, b.head
if a.head is smaller or b.is_empty then
append a.head to output
a.drophead
else if b.head is smaller or a.is_empty then
append b.head to output
b.drophead
else
append b.head to output
b.drophead
a.drophead

Categories

Resources