1

Let's assume we want to create some very readable code which does the next:

  1. iterates through all elements of list
  2. changes all of them equally except only one specific element (even though more than one element can satisfy search criteria)
  3. return this list with changed elements
  4. uses _.underscore power

For example, i want to add property isChosen with true value only to the first element which has hasRoom property, other element should get false value for this new property hasRoom

Here is code created using _.underscore lib:

  var i = 0
  var changedList = []
  _.each(list, function(el) {
    if (el.hasRoom && i === 0) {
      _.extend(el, { isChosen: true })
      i++
    } else {
      _.extend(el, { isChosen: false })
    }
    changedList.push(el)
  })

This code works, but I think there can be better way of making same, maybe with _.chain()?

So, as input we can have

[
    {hasRoom: false}, 
    {hasRoom: true}, 
    {hasRoom: true}
]

And as result we should get

[
    {hasRoom: false, isChosen: false}, 
    {hasRoom: true,  isChosen: true}, 
    {hasRoom: true,  isChosen: false}
]
2
  • Do you have to create a new object? As for the code-readability tag, why use a number when you can use a boolean? Commented Jan 21, 2014 at 14:26
  • @user1066946 this code chunk is inside of function addPropertyToOnlyOneElement(list) and it should return changed list Commented Jan 21, 2014 at 14:31

5 Answers 5

2
var _ = require("underscore");
var data = [{hasRoom: true}, {hasRoom: true}, {}], found = false;

_.each(data, function(currentObject) {
    _.defaults(currentObject, {isChosen: false});
    if (found === false && currentObject.hasRoom) {
        found = currentObject.isChosen = true;
    }
});

console.log(data);

Output

[ { hasRoom: true, isChosen: true },
  { hasRoom: true, isChosen: false },
  { isChosen: false } ]
Sign up to request clarification or add additional context in comments.

Comments

1

This solution modifies the function that sets the isChosen:

function notChosen(place){
  return false;
}

function chosen(place){
  if( !place.hasRoom ) return notChosen(place);
  setChosen = notChosen;
  return true;
}

var setChosen = chosen;

_.each(places, function(place){
    place.isChosen = setChosen(place);
});

The if statement will only be executed until the first place with a room is found.

Comments

1

You can use a method typically called first to find the first element that matches your criteria:

function first (array, filter_function) {
    var index, len = array.length;
    for (index = 0; index < len; index += 1) {
        if (filter_function(array[index], index)) {
            return index;
        }
    }
    return -1;
}

rooms = [
    { hasRoom: false }, 
    { hasRoom: true }, 
    { hasRoom: true }
];

free_room = first(rooms, function (room) {
    return room.hasRoom;
});
if (free_room === -1) {
    throw "Dilbert lied!";
}
rooms[free_room].isChosen = true;

Note that _js also defines a first function on an Array, but it's more for a cat or head function. You can argue about semantics.

6 Comments

You are putting everything in the global scope, also there is a for each in JavaScript
There is no for each in JavaScript; only for-in. We're dealing with Arrays not Objects, so for-in is not needed. This is a proof-of-concept code to demonstrate how first works, take your global scope concerns to places where they are relevant. Your comment is wrong and misleading.
Are you sure your code has better readability then code in question?
There is Array.prototype.forEach and also the for in loop, before you edited the question you put every variable in the answer on the global scope, and the answer is twice as long as the question.
Sorry I use Chrome, didn't know you were running IE8
|
1

In a functional style, you'd do this:

// type: [a] -> (a -> Bool -> b) -> (a -> Bool) -> [b]
function changeFirstinList(list, change, predicate) {
    var first = true;
    return _.map(list, function(el) {
        return change(el, first && !(first = !predicate(el)));
    });
}

changeFirstinList([
    {hasRoom: false}, 
    {hasRoom: true}, 
    {hasRoom: true}
], function(el, isFirstwithpred) {
    return {hasRoom: el.hasRoom, isChosen: isFirstwithpred};
}, function(el) {
    return el.hasRoom;
});

Comments

0

Finally my code started to look like this:

function addChosenPropertyToFirstElementWithRooms(hotels) {
  var found = false
  return _.map(list, function(el) {
    return _.merge(el, { isChosen: isFirstElementWithRooms(el) })
  })

  function isFirstHotelWithRooms(el) {
    return found ? false : found = el.hasRoom
  }
}

And usage of function is easy

var changedList = addChosenPropertyToFirstElementWithRooms(originalList)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.