How to find duplicated title values in a map - javascript

I'm new to Javascript, come from Java, this is less intuitive for me.
I would like to check for duplication of the title value and concatenate to the duplicated title the producer name
My idea is to sort the values and then check each one with is next for duplication
Can you suggest me how to implement this kind of solution?
function getItems(itemKeys, itemSortOrders, itemsMap)
{
var items = _.map(itemKeys, function(itemKey, index) {
var item = itemsMap[itemKey];
return _.extend({
key: itemKey,
title: item.title,
imageURL: item.imageURL,
formattedPrice: utils.formatMoney(item.price),
producerKey: item.producerKey,
producerTitle: item.producerTitle,
allowOrder: true,
sortOrder: itemSortOrders[index]
}, calculateItemDetails(item.deliveryDayAvailable, item.deliveryDayStatus, item.deliveryDayUsageCount));
});
items = _.compact(items);
return items;
}
Thanks

You can test if item have duplicates with this function, it use filter to find the same items and check if the length is larger then 1.
function haveDuplicates(itemKeys, itemsMap, itemKey) {
var item = itemsMap[itemKey];
var dups = itemKeys.filter(function(key) {
return itemsMap[key] == item;
});
return dups.length > 1;
}
var itemsMap = {
'foo': 'Lorem',
'bar': 'Lorem',
'baz': 'Ipsum',
'quux': 'Dolor'
};
var output = document.getElementById('output');
var itemKeys = Object.keys(itemsMap);
itemKeys.map(function(key) {
output.innerHTML += itemsMap[key] + ' ' +
(haveDuplicates(itemKeys, itemsMap, key) ? 'have' : 'don\'t have') + '\n';
});
<pre id="output"></pre>

SO this is what i did eventually and this worked
var duplicateMap = {};
_.each(itemsMap, function(item) {
var title = item.title.trim();
if (duplicateMap[title]) {
duplicateMap[title] = 2;
}
else {
duplicateMap[title] = 1;
}
});

Related

Count duplicate property values of a nested Javascript object

I'm feeding in information in from an API with the format -
Records = { 0: {fields: {name: "nameExample" place: "placeExample"} }
{1: {fields: {name: "nameExample" place: "placeExample"} }
etc etc
I'm looking for a way to count duplicates of the 'place' field in the format: Scotland(4), London(6) etc. where the number is the amount of times it repeats.
The closest I've got as of yet is counting the letters in each place name, but I'm not sure what I'm doing wrong. I think if I can get them in the same array then it would be simple, but when I tried to do that it put each place in a different array.
Also must be vanilla javaScript, no jQuery. Many thanks!
var URL = new Array();
URL[0] = "https://www.vam.ac.uk/api/json/museumobject/search?q=a&limit=45";
URL[1] = "https://www.vam.ac.uk/api/json/museumobject/search?q=a&limit=45&offset=45";
URL[2] = "https://www.vam.ac.uk/api/json/museumobject/search?q=a&limit=45&offset=90";
var nRequest = new Array();
for (var i=0; i<3; i++){
(function(i) {
nRequest[i] = new XMLHttpRequest();
nRequest[i].open("GET", URL[i], true);
nRequest[i].onreadystatechange = function (oEvent) {
if (nRequest[i].readyState === 4) {
if (nRequest[i].status === 200) {
var data = JSON.parse(nRequest[i].responseText);
var url = 'http://media.vam.ac.uk/media/thira/collection_images/';
for (let key in data.records) {
let value = data.records[key];
let image = value.fields.primary_image_id;
let res = image.substr(0, 6);
document.querySelector(".map").innerHTML += '<div class="' + value.fields.place + ' map"> ' + value.fields.place + ' <br> </div>';
}
} else {
console.log("Error", nRequest[i].statusText);
}
}
//
};
nRequest[i].send(null);
})(i);
};
If thats the data structure you can solve it like so:
const Records = [
{fields: {name: "nameExample", place: "placeExample"}},
{fields: {name: "nameExample", place: "placeExample"}}
];
const result = Records.reduce((acc, {fields}) => {
if(acc[fields.place]) acc[fields.place]++;
else acc[fields.place] = 1;
return acc;
}, {})
console.log(result);
You can use the reduce function to convert the array data.records to an object with the place as key and number of occurrences as value.
data.records.reduce((accum, currVal) => {
if (!(currVal.fields.place in accum)){
accum[currVal.fields.place] = 0;
}
accum[currVal.fields.place] += 1;
return accum;
}, {});

How do I search for a specific value in an array of objects in Javascript?

The array of objects I want to filter/search looks somewhat like this:
var array = [
{
id: 62,
title: "sometitle",
text: „aaaaaaaaaaaaaaa“,
keywords: ["other", "Party", "Shanghai"],
read: false
},
{
id: 63,
title: "othertitle",
text: "bbbbbbbbbbbbbbbbbb",
keywords: ["Party", "Shanghai", "Seo-Yeon"],
read: false
},
{
id: 64,
title: "againothertitle",
text: "ccccccccccccccccccc",
keywords: ["Chinesisch", "Alltag", "other"],
read: false
}];
I want to select one random value of an object and filter the whole array to find other objects that share this one specific value - except for the current object of course. To be more precise I want to filter the array with one of the „keywords“. The current object should be spliced from the array.
Also underneath the current object I want to display buttons that each contain the title of the object that shares the keyword. When the user clicks on one oft he buttons the according object should be selected, shown, again sliced from array. Again one of this selected object’s keywords should be randomly selected to filter the rest of the array with.
This is what the implementation should look like:it looks right but doesnt behave right^^
This is the html:
<body>
<div id="container-fluid">
<h2 id="title"></h2>
<div id="output"></div>
<div id="button-container"></div>
</div>
<script type="text/javascript" src="src/js/snu.js"></script>
<script type="text/javascript" src="src/js/index.js"></script>
</body>
And this the JS:
var startKeywords = [];
var btnContainer = document.getElementById('button-container');
var title = document.getElementById('title');
var output = document.getElementById('output');
var container = document.getElementById('container-fluid');
var result = [];
var nextSnusButtons = [];
var button = [];
//select a random SNU
var randomIndex = Math.floor(Math.random() * snus.length);
(function first() {
//showing the first random scene
var start = snus[randomIndex];
title.innerHTML = start.title;
title.id = start.id;
output.innerHTML = start.text;
start.read = true;
cache.push(start);
startKeywords = start.keywords;
var randomStartKeyIndex = Math.floor(Math.random() * startKeywords.length);
var randomStartKey = startKeywords[randomStartKeyIndex];
//create a filter
function filterNextSet(val){
var randomValueKeyIndex = Math.floor(Math.random() * val.keywords.length);
var randomValueKey = val.keywords[randomValueKeyIndex];
if (randomStartKey === val.keywords[0] || randomStartKey ===val.keywords[1] || randomStartKey === val.keywords[2] || randomStartKey === val.keywords[3] && randomStartKey.read === false) {
return val
}
}
//apply filter
result = snus.filter(filterNextSet);
var resultFirst = result[0];
var resultLastIndex = result.length -1;
var resultLast = result[resultLastIndex];
var resultRandomIndex = Math.floor(Math.random() * result.length);
var resultRandom = result[resultRandomIndex];
//generate HTML
if(resultFirst.id || resultRandom.id || resultLast.id) {
nextSnusButtons.push(resultFirst, resultRandom, resultLast);
nextSnusButtons.forEach(function(nextSnu) {
button = document.createElement('button');
button.id = nextSnu.id;
button.innerHTML = nextSnu.title;
btnContainer.append(button);
});
}
})();
I have been using plain javascript to solve this problem for days but I only find myself in Spghetti code. I feel like I am constnatly repeating stuff and that this is not gonna end. I would really appreciate your help! Should I use React instead??
Thank You very much in advance!
Ok,
Here a simple example of doing the first part (filtering the data).
In this example at the end I'm just showing the match for every combo, in your case you can just call the getForArrayItem with your random number.
var array = [
{
id: 62,
title: "sometitle",
text: "aaaaaaaaaaaaaa",
keywords: ["other", "Party", "Shanghai"],
read: false
},
{
id: 63,
title: "othertitle",
text: "bbbbbbbbbbbbbbbbbb",
keywords: ["Party", "Shanghai", "Seo-Yeon"],
read: false
},
{
id: 64,
title: "againothertitle",
text: "ccccccccccccccccccc",
keywords: ["Chinesisch", "Alltag", "other"],
read: false
}];
function getForArrayItem(idx) {
let
item = array[idx],
iset = new Set(item.keywords);
return array.filter((i) => {
//first don't include self
if (i === item) return false;
//now see if any of the keyword exist in this
return i.keywords.some(x => iset.has(x));
});
}
array.forEach((item, ix) => {
console.log('ITEM ' + item.id + ' = ' +
getForArrayItem(ix).map((i)=>i.id).join(', '));
});
Here is a snippet just using ES5 constructs. Also I've not used any forEach, some, filter.. just for loops..
var array = [
{
id: 62,
title: "sometitle",
text: "aaaaaaaaaaaaaa",
keywords: ["other", "Party", "Shanghai"],
read: false
},
{
id: 63,
title: "othertitle",
text: "bbbbbbbbbbbbbbbbbb",
keywords: ["Party", "Shanghai", "Seo-Yeon"],
read: false
},
{
id: 64,
title: "againothertitle",
text: "ccccccccccccccccccc",
keywords: ["Chinesisch", "Alltag", "other"],
read: false
}];
function getForArrayItem(idx) {
var
item = array[idx],
iset = {},
ret = [],
l;
//first lets store a set of fast lookup keywords
//we could use indexOf, but this should be faster
for (l = 0; l < item.keywords.length; l++)
iset[item.keywords[l]] = true;
//now loop through all arrays
for (l = 0; l < array.length; l ++) {
//lets not include our self
if (l === idx) continue;
var aitem = array[l], any = false;
//now ltes see if any of keywords exist in our iset lookup
for (var lc = 0; lc < aitem.keywords.length; lc++) {
var keyword = aitem.keywords[lc];
if (iset[keyword]) {
ret.push(aitem);
break;
}
}
if (any) ret.push(item);
}
return ret;
}
for (var ix = 0; ix < array.length; ix ++) {
var items = getForArrayItem(ix);
var id_items = [];
for (var ll = 0; ll < items.length; ll++) id_items.push(items[ll].id);
console.log('ITEM ' + array[ix].id + ' = ' + id_items.join(', '));
}
This is my solution:
function selectRandomKeyword(obj) {
const max = obj['keywords'].length;
const random = Math.floor(Math.random() * max);
return obj['keywords'][random];
}
function findObjsWithKeyword(array, keyword) {
const foundArray = [];
array.forEach(obj => {
if (obj['keywords'].includes(keyword)) {
foundArray.push(obj);
}
});
return foundArray;
}
const selectedObj = (array[0]);
const keyword = selectRandomKeyword(selectedObj);
const newObjs = findObjsWithKeyword(array, keyword);
So newObjs will have your new array with only the matching randomly selected keyword. And if you want to remove the selectedObj:
var index = newObjs.indexOf(selectedObj);
newObjs.splice(index, 1);
newObjs;

Unable to bind data to dropdown-multiselect in angularjs

Please check the plunker. I'm not able to bind the data which come from server like this(["Monday","Tuesday"]).
I know data binds when it is like
$scope.selectedUser = [{ id: 2, name: 'Monday' },
{id: 3,name:'Tuesday'}];.
I want to make ["Monday","Tuesday"] to [{ id: 2, name: 'Monday' },{id: 3,name:'Tuesday'}] in javascript so that it would bind in dropdown.
Please help me solve this issue.
demo plunker
//This is the ng filter to create id as you get the data from server
app.filter('createId',function(){
return function(arr){
var result = [];
for(var i = 0;i < arr.length;i++){
var id = arr[i].substring(0,3);
var obj = {'id':id,'name':arr[i]};
result.push(obj);
}
return result;
}
});
//In js controller you can costomize your array of user by using ng-filter like this
$scope.users = $filter('createId')($scope.users);
//This is because index will be changed every time so this can't be used as ID
$scope.selectedUser = $filter('createId')($scope.selectedUser);
Achieve your json using below code
var temp = ["Monday", "Tuesday"]
var result = "[";
for (var i = 0; i < temp.length; i++) {
if(i!=temp.length-1)
{
result += "{id:'" + i + "',name:'" + temp[i] + "'},";
}
else
{
result += "{id:'" + i + "',name:'" + temp[i] + "'}";
}
alert(result);
}
console.log(result+"]")
You can do this
$scope.selectedUsers=[];
//serverData is data from server
for(var i =0 ; i < serverData.length ; i++){
$scope.selectedUsers.push({id:i , name: serverData[i]})
}
And in your angular
$scope.doSelectedUser = function () {
$scope.selectedUser = $scope.selectedUsers;
}
$scope.selectedUser should have a reference to the actual objects in $scope.users. for example, this is what i had to change in your plunker to bind it with the users match the user names list:
var defaultSelectedUsers = ["Sunday","Tuesday"];
$scope.users = [
{ id: 1, name: 'Sunday' },
{ id: 2, name: 'Monday' },
{ id: 3, name: 'Tuesday' } ];
$scope.selectedUser = $scope.users.filter(function(user){
return defaultSelectedUsers.indexOf(user.name) != -1;
});
Online Demo - http://plnkr.co/edit/3TOkZEaZVSxtpNbFaNkg?p=preview
You mentioned you want to do it from your server's response. it's unclear how you want to decide which one to select, assuming you rely on a default selected users by name according your example.
This is how you handle a server's response and modify the new selected users:
var defaultSelectedUsers = ["Sunday","Tuesday"];
$http.get('/api/v1/users')
.success(function(users)){
$scope.users = users;
$scope.selectedUser = users.filter(function(user){
return defaultSelectedUsers.indexOf(user.name) != -1;
});
});
You may change the filter condition to what makes sense for you

Removing an item from a two dimensional Javascript Array

I have a simple array like this:
var CutRoadArray = [
['Location', '_Location'],
['Applicant Info', '_ApplicantInfo'],
['Details', '_ApplicationDetails'],
['Bond Info', '_BondInfo'],
['Attachments', '_Attachments'],
['Review', '_ReviewA']
];
I would like to check if this array contains the entry
['Bond Info', '_BondInfo'],
And if it does, remove it. In a separate scenario, I would like to search for the same, and if it doesnt exist, add it at a certain index.
I have tried various ways, none worked. Any help will be much appreciated.
One of the ways I have tried to accomplish this is:
Array.prototype.remove = function () {
var what, a = arguments, L = a.length, ax;
while (L && this.length) {
what = a[--L];
while ((ax = this.indexOf(what)) !== -1) {
this.splice(ax, 1);
}
}
return this;
};
function indexOfRowContainingId(id, matrix) {
var arr = matrix.filter(function (el) {
return !!~el.indexOf(id);
});
return arr;
}
Then calling something like:
var bond = indexOfRowContainingId('_BondInfo', CutRoadArray);
if (bond.length > 0) {
var ar = CutRoadArray.remove("['Bond Info', '_BondInfo']");
console.log(ar);
}
Try this:
var CutRoadArray = [
['Location', '_Location'],
['Applicant Info', '_ApplicantInfo'],
['Details', '_ApplicationDetails'],
['Bond Info', '_BondInfo'],
['Attachments', '_Attachments'],
['Review', '_ReviewA']
];
var testElem = ['Bond Info', '_BondInfo'];
for(var i=0; i<CutRoadArray.length; i++){
var temp = CutRoadArray[i].toString();
if(temp === testElem.toString()){
//remove element from array
CutRoadArray.splice(i, 1);
break;
}
}
console.log(CutRoadArray);
This function has your desired functionality:
function testArray(test, array){
return array.filter(function(x){
return x.toString() != test;
})
}
testArray(['Bond Info', '_BondInfo'], CutRoadArray)

How to add the numeric values of properties of a nested array?

I'm trying to figure out how to make a function that adds the values of the elements chosen by the user and be able to display the results via prompt and console.log. Also, I'm wondering if there is a way to do it in which I don't need to specify the elements selected in order for the function to find within the code which elements were selected and execute the addition function. Because obviously if the list of options were longer I wouldn't want to have to make a new function for each potential combination of selections. As a side note, I guess the same problem would apply to the if statements, would switch statements be the most efficient way to tackle needs for "DRY" code in that instance?
My javascript code: Please assume that the user selects only the first elements of the nested arrays. Also, that term "one" is worth $8.
var selection = new Array (3);
selection[0] = new Array ('$1', '$2', '$3', '$4', '$5', '$6', '$7', '$8');
selection[1] = new Array ('phone1', 'phone2', 'phone3');
selection[2] = new Array ('one', 'two', 'three');
function pickPhone () {
var yourPhone = prompt("pick phone: phone1: $1, phone2: $2, phone3: $3");
if (yourPhone == selection[1][0]) {
console.log(selection[1][0] + " will cost: " + selection[0][0]);
alert(selection[1][0] + " will cost: " + selection[0][0]);
pickTerm ();
} if (yourPhone == "phone2") {
alert(selection[1][1] + " will cost: " + selection[0][1]);
} if (yourPhone == "phone3") {
alert(selection[1][2] + " will cost: " + selection[0][2]);
}
}
function pickTerm () {
var yourTerm = prompt("pick term: one, two or three?");
if (yourTerm == selection[2][0]) {
alert("Your total so far is: ??");
}
}
pickPhone ();
Any help is greatly appreciated, thank you.
A solution that keeps your arrays
http://jsfiddle.net/OxyDesign/o10ezyun/
JS
var selection = new Array(3);
selection[0] = new Array(1,2,3,4,5,6,7,8);
selection[1] = new Array('phone1', 'phone2', 'phone3');
selection[2] = new Array('one', 'two', 'three');
var firstValue;
function pickPhone() {
var yourPhone = prompt("pick phone: phone1: $1, phone2: $2, phone3: $3"),
index = selection[1].indexOf(yourPhone);
if(!~index){
pickPhone();
}else{
firstValue = selection[0][index];
alert(selection[1][index] + " will cost: $" + firstValue);
pickTerm();
}
}
function pickTerm() {
var yourTerm = prompt("pick term: one, two or three?"),
index = selection[2].indexOf(yourTerm),
finalValue = '$'+(firstValue+selection[0][index]);
if(!~index){
pickTerm();
}else{
alert("Your total so far is: "+finalValue);
}
}
pickPhone();
I'am not sure what problem you are actually solving.
How long these lists are (phones, costs, etc)?
What type of mapping is set for those items?
For now I'd recommend to merge corresponding values in objects like this:
// item -> cost
var phones = [
{title: 'phone1', cost: '$1'},
{title: 'phone2', cost: '$2'},
{title: 'phone3', cost: '$3'}
],
terms = [
{title: 'one', cost: '$8'},
{title: 'two', cost: '$2'},
{title: 'three', cost: '$3'}
],
phonesListWithCosts = (function(list) {
return list.reduce(function(memo, item) {
return memo + item.title + ': ' + item.cost;
}, '');
}(phones)),
termsList = (function(list) {
return list.reduce(function(memo, item) {
return memo + ', ' + item.title;
}, '');
}(terms)),
findBy = function(array, property, value) {
return array.filter(function(item) {
return item[property] === value;
})[0];
},
getItem = function(list, promptMessage) {
var selectedItemTitle = prompt(promptMessage);
return findBy(list, 'title', selectedItemTitle);
},
getItemCost = function(item) {
return parseInt(item.cost.replace(/\D/g, ''), 10);
},
pickPhone = function() {
var selectedPhone = getItem(phones, 'pick phone: ' + phonesListWithCosts),
firstPhone = phones[0],
message;
if (selectedPhone) {
message = [selectedPhone.title, 'will cost:', selectedPhone.cost].join(' ');
console.log(message);
alert(message);
if (selectedPhone === firstPhone) {
pickTerm(getItemCost(selectedPhone));
}
}
},
pickTerm = function(accumCost) {
var selectedTerm = getItem(terms, 'pick term: ' + termsList),
totalCost,
message;
if (selectedTerm) {
totalCost = accumCost + getItemCost(selectedTerm);
message = 'Your total so far is: $' + totalCost;
alert(message);
}
};
pickPhone();
jsbin demo.

Categories

Resources