Question

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}
]
Was it helpful?

Solution 2

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.

OTHER TIPS

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

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.

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

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top