Remove duplicates from array of arrays by the first object - javascript

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>

Related

Nested JSON string to dictionary with object to be used as key

I am loading a nested JSON query and I am trying to create a dictionary that will have a specific object as a key. Sp, in my case, the key will be the country and then each dictionary value will have the date and the number. I have made a progress but I am stuck at the final function. My snippet prints all the steps in the HTML part.
So, I am actually trying to convert this:
2018-05-01,Italy,25,2018-04-01,Italy,37,2018-05-01,France,30,2018-04-01,France,90
to this:
{"Italy":[{"Date":"2018-04-01","num":37},{"Date":"2018-05-01","num":25}],"France":[{"Date":"2018-04-01","num":90},{"Date":"2018-05-01","num":30}]}
which will then be converted to:
[
{
"Value": "Italy",
"num": 37,
"num2": 25
},
{
"Value": "France",
"num": 90,
"num2": 30
}
]
where num will have the number of the first date and num2 will have the number of the second date.
Also, feel free to provide a completely different solution if you believe that my steps are too many.
var json_data = {"headers":["Month","Country","Number"],"rows":[["2018-05-01","Italy",25],["2018-04-01","Italy",37],["2018-05-01","France",30],["2018-04-01","France",90]
]};
var dataRows = json_data.rows;
document.getElementById("yellow").innerHTML = dataRows;
//Returns unique values of a specific object of a JSON string
uniqueValues = (data,objectNum) => {
var uniqueValues = [];
data.forEach(function(item) {
var value = item[objectNum];
if (uniqueValues.indexOf(value) !== -1)
return false;
uniqueValues.push(value);
});
return uniqueValues;
}
var uniqueCountries = uniqueValues(dataRows,1);
document.getElementById("green").innerHTML = uniqueCountries;
var uniqueDates = uniqueValues(dataRows,0);
document.getElementById("blue").innerHTML = uniqueDates;
//Create dictionary function (transformed JSON)
createDict = (data,objectNum) => {
var dict =[];
var num = 0;
var num2 = 0;
for (i = 0; i < dataRows.length; i++)
{
var object = {"Date": dataRows[i][0].slice(0,10), "Value": dataRows[i][1], "num": dataRows[i][2]};
dict.push(object);
}
return dict;
}
var dictData = createDict(dataRows,2);
document.getElementById("orange").innerHTML = JSON.stringify(dictData);
//Function that will return the final output
function test (){
var sumMetric = {};
dictData.forEach(function(d) {
uniqueCountries.forEach(function(country) {
console.log(d.Date);
//d.num = +d.num;
sumMetric[country] = [];
uniqueDates.forEach(function(element) {
sumMetric[country].push({Date: d.Date, num: d.num});
});
});
});
document.getElementById("red").innerHTML =JSON.stringify(sumMetric);
return sumMetric;
}
test();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
<h4>Original JSON</h4>
<div style="background:yellow;" id="yellow"></div>
<h4>Unique Countries</h4>
<div style="background:green; color:white" id="green"></div>
<h4>Unique Dates</h4>
<div style="background:blue; color:white" id="blue"></div>
<h4>Dictionary Creation</h4>
<div style="background:orange;" id="orange"></div>
<h4>Wrong Output</h4>
<div style="background:red;" id="red"></div>
<h4>Expected Output</h4>
<div style="background:purple; color:white" id="purple">{"Italy":[{"Date":"2018-04-01","num":37},{"Date":"2018-05-01","num":25}],"France":[{"Date":"2018-04-01","num":90},{"Date":"2018-05-01","num":30}]}</div>
<h4>Final Output that I need</h4>
<div style="background:grey; color:black" id="purple">[
{
"Value": "Italy",
"num": 37,
"num2": 25
},
{
"Value": "France",
"num": 90,
"num2": 30
}
]<div>
Assuming that the array of arrays in your snippet is the correct input dataset to start with (rather than the string posted at the beginning of your question)...
If the original array is guaranteed to only have 2 sub-arrays for each country, then you can use reduce to group it into an object by country key and then compare the array pairs in the grouped object to produce the desired results. For example:
const data = [["2018-05-01", "Italy", 25], ["2018-04-01", "Italy", 37], ["2018-05-01", "France", 30], ["2018-04-01", "France", 90]];
const groups = data.reduce((acc, arr) => {
if (acc.hasOwnProperty(arr[1])) {
acc[arr[1]].push(arr);
} else {
acc[arr[1]] = [arr];
}
return acc;
}, {});
let results = [];
for (const g in groups) {
const obj = { value: g };
const a = groups[g][0];
const b = groups[g][1];
if (a[0] <= b[0]) {
obj.num = a[2];
obj.num2 = b[2];
} else {
obj.num = b[2];
obj.num2 = a[2];
}
results.push(obj);
}
console.log(results);
If the original array is not guaranteed to have only 2 sub-arrays for each country, then you would have to modify the for...in loop above to loop through and compare multiple country arrays (rather than just comparing a single pair) and update the result objects as needed.

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}
];

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 convert the hashmap value in array in javascript?

Consider the following json value:
{"Operator":{"DT5241":{"name":"LESLIE, Alec "},"DT3709":{"name":"DAWSON, Peter"},"DT4206":{"name":"PEPWORTH, Jasmine"}
How can I convert this to array?
I have tried doing this: Operator being my arr2[3]
var array = $.map(arr2[3], function(value, index) {
return [value];
});
But it does not help. It gives value as this:
0:Object
DT5241:Object
DT3709:Object
DT4206:Object
I need only array list.
This works.
But its not inserting value to my table:
var dataArray2 = [['TruckName', 'OperatorName']];
for (var i = 3; i < arr2.length; i++) {
for (var j = 0; j < array.length; j++){
dataArray2.push([array[i], array[i].name]);
}
I think the problem is arr2[3] is the object with 1 item that is the object with Operator key, so you need to iterate through arr2[3].Operator
var arr2 = [];
arr2[3] = {
"Operator": {
"DT5241": {
"name": "LESLIE, Alec "
},
"DT3709": {
"name": "DAWSON, Peter"
},
"DT4206": {
"name": "PEPWORTH, Jasmine"
}
}
}
var array = $.map(arr2[3].Operator, function(value, key) {
var obj = {};
obj[key] = value;
return obj;
});
console.log(array)
$('#result').html(JSON.stringify(array))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="result"></div>

How do I slice an array from an array of object literals?

I have this array, in which each index contains an object literal. All of the object literals have the same properties. Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals.
My idea is to sort the array, and slice it into a new array...
Here is the array:
var arr = [];
arr[0] =
{
country: "United States",
num: 27
};
arr[1] =
{
country: "Australia",
num: 5
};
arr[2] =
{
country: "United States",
num: 7
};
So, I want to create a new array containing only those objects where the property country is "United States". This is my crazy idea so far, which doesn't work:
function getNewArray(arr)
{
var arr2 = [];
for(var key in arr)
{
for(var i = 0; i < arr.length - 1; i++)
{
if(arr.hasOwnProperty(key) && arr[i].name == arr[i + 1].name)
{
arr2[i] = arr.slice(key);
}
}
}
return arr2;
}
var arr3 = getNewArray(arr).sort();
"I want to create a new array containing only those objects where the property country is "United States""
This is exactly what the Array.filter() method is for:
var filteredArray = arr.filter(function(val, i, a) {
return val.country==="United States";
});
Note that the .filter() method isn't available in IE before version 9, but the MDN page I linked to above shows you exactly how to implement it so reading that page should in itself answer your question.
Note also that in the (non-working) code in the question, your two for loops are basically doing the same thing as each other because they're both iterating over arr, so it doesn't make sense to nest them like that. You shouldn't use a for..in loop on an array, but if you do the key values will be the numeric indexes, it doesn't somehow pick up the properties of the object stored at each index.
EDIT:
"Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals."
OK, re-reading this I guess you didn't really want to select elements by specifying a country, you wanted to select elements for any country that had duplicate entries? So if there were another three elements that all had "New Zealand" you'd want to select them in addition to the "United States" ones? If so, you could do something like this:
var countryCount = {},
i;
for (i = 0; i < arr.length; i++)
if (countryCount.hasOwnProperty(arr[i].country)
countryCount[arr[i].country]++;
else
countryCount[arr[i].country] = 1;
var filteredArr = arr.filter(function(val, i, a) {
return countryCount[val.country] > 1;
});
var getCountry = function (country) {
var out = [];
for (var i = 0, len = arr.length; i < len; i++)
if (arr[i].country === country) out.push(arr[i]);
return out;
};
Demo: http://jsfiddle.net/YwytD/1/
There is a simpler way of doing this, I think this is what you want
var new_arr = arr.filter(function(obj){ return obj['country'] === 'United States'; })
This will filter your results into new_arr
Of course you can make it better and more generic than just 'United States'
Edit:
Whoops, got your question just now
Answer: Nested Filters :)
function getKeyStuff(key) {
return arr.filter( function(obj) {
var new_arr = arr.filter( function(inner_obj) {
return inner_obj[key] === obj[key];
});
return new_arr.length > 1;
});
}
Here is my solution:
// assuming arr is already set
var num_obj = arr.length;
var obj_by_country = {}, obj;
for (var i = 0; i < num_obj; i++) {
obj = arr[i];
if (!obj_by_country[obj.country]) {
obj_by_country[obj.country] = [];
}
obj_by_country[obj.country].push(obj);
}
// build final array
var final_array = [];
for (i in obj_by_country) {
if (obj_by_country[i].length > 1) {
final_array.push(obj_by_country[i]);
}
}
Can you use JQuery?
var arr = [];
arr[0] = { country: "United States", num: 27 };
arr[1] = { country: "Australia", num: 5 };
arr[2] = { country: "United States", num: 7 };
var newArray = [];
$.each(arr, function(){
if(this.country == "United States")
newArray.push(this);
});
getByKey = function(arr, key, value) {
var results = [];
for(var i = 0; i < arr.length; i++) {
if(arr[i][key] === value) {
results.push(arr[i]);
}
}
return results
}
here's a working example
http://jsfiddle.net/michaelghayes/UyHYz/2/

Categories

Resources